hanami-controller 1.3.3 → 2.0.0.alpha4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +299 -537
  5. data/hanami-controller.gemspec +4 -3
  6. data/lib/hanami/action/application_action.rb +131 -0
  7. data/lib/hanami/action/application_configuration/content_security_policy.rb +118 -0
  8. data/lib/hanami/action/application_configuration/cookies.rb +29 -0
  9. data/lib/hanami/action/application_configuration/sessions.rb +46 -0
  10. data/lib/hanami/action/application_configuration.rb +90 -0
  11. data/lib/hanami/action/base_params.rb +2 -2
  12. data/lib/hanami/action/cache/cache_control.rb +4 -4
  13. data/lib/hanami/action/cache/conditional_get.rb +3 -1
  14. data/lib/hanami/action/cache/directives.rb +1 -1
  15. data/lib/hanami/action/cache/expires.rb +3 -3
  16. data/lib/hanami/action/cache.rb +1 -139
  17. data/lib/hanami/action/configuration.rb +428 -0
  18. data/lib/hanami/action/cookie_jar.rb +3 -3
  19. data/lib/hanami/action/cookies.rb +3 -62
  20. data/lib/hanami/action/csrf_protection.rb +214 -0
  21. data/lib/hanami/action/flash.rb +102 -207
  22. data/lib/hanami/action/glue.rb +5 -31
  23. data/lib/hanami/action/halt.rb +12 -0
  24. data/lib/hanami/action/mime.rb +78 -485
  25. data/lib/hanami/action/params.rb +2 -2
  26. data/lib/hanami/action/rack/file.rb +1 -1
  27. data/lib/hanami/action/request.rb +30 -20
  28. data/lib/hanami/action/response.rb +193 -0
  29. data/lib/hanami/action/session.rb +11 -128
  30. data/lib/hanami/action/standalone_action.rb +578 -0
  31. data/lib/hanami/action/validatable.rb +1 -1
  32. data/lib/hanami/action/view_name_inferrer.rb +46 -0
  33. data/lib/hanami/action.rb +129 -73
  34. data/lib/hanami/controller/version.rb +1 -1
  35. data/lib/hanami/controller.rb +0 -227
  36. data/lib/hanami/http/status.rb +2 -2
  37. metadata +44 -26
  38. data/lib/hanami/action/callable.rb +0 -92
  39. data/lib/hanami/action/callbacks.rb +0 -214
  40. data/lib/hanami/action/configurable.rb +0 -50
  41. data/lib/hanami/action/exposable/guard.rb +0 -104
  42. data/lib/hanami/action/exposable.rb +0 -126
  43. data/lib/hanami/action/head.rb +0 -121
  44. data/lib/hanami/action/rack/callable.rb +0 -47
  45. data/lib/hanami/action/rack/errors.rb +0 -53
  46. data/lib/hanami/action/rack.rb +0 -411
  47. data/lib/hanami/action/redirect.rb +0 -59
  48. data/lib/hanami/action/throwable.rb +0 -169
  49. data/lib/hanami/controller/configuration.rb +0 -763
  50. data/lib/hanami-controller.rb +0 -1
@@ -0,0 +1,428 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/configurable"
4
+ require "hanami/utils/kernel"
5
+ require "pathname"
6
+ require_relative "mime"
7
+
8
+ module Hanami
9
+ class Action
10
+ class Configuration
11
+ include Dry::Configurable
12
+
13
+ # Initialize the Configuration
14
+ #
15
+ # @yield [config] the configuration object
16
+ #
17
+ # @return [Configuration]
18
+ #
19
+ # @since 2.0.0
20
+ # @api private
21
+ def initialize(*)
22
+ super
23
+ yield self if block_given?
24
+ end
25
+
26
+ # Returns the list of available settings
27
+ #
28
+ # @return [Set]
29
+ #
30
+ # @since 2.0.0
31
+ # @api private
32
+ def settings
33
+ self.class.settings
34
+ end
35
+
36
+ # @!method handled_exceptions=(exceptions)
37
+ #
38
+ # Specifies how to handle exceptions with an HTTP status
39
+ #
40
+ # Raised exceptions will return the corresponding HTTP status
41
+ #
42
+ # @param exceptions [Hash{Exception=>Integer}] exception classes as
43
+ # keys and HTTP statuses as values
44
+ #
45
+ # @return [void]
46
+ #
47
+ # @since 0.2.0
48
+ #
49
+ # @example
50
+ # configuration.handled_exceptions = {ArgumentError => 400}
51
+ #
52
+ # @!method handled_exceptions
53
+ #
54
+ # Returns the configured handled exceptions
55
+ #
56
+ # @return [Hash{Exception=>Integer}]
57
+ #
58
+ # @see handled_exceptions=
59
+ #
60
+ # @since 0.2.0
61
+ setting :handled_exceptions, default: {}
62
+
63
+ # Specifies how to handle exceptions with an HTTP status
64
+ #
65
+ # Raised exceptions will return the corresponding HTTP status
66
+ #
67
+ # The specified exceptions will be merged with any previously configured
68
+ # exceptions
69
+ #
70
+ # @param exceptions [Hash{Exception=>Integer}] exception classes as keys
71
+ # and HTTP statuses as values
72
+ #
73
+ # @return [void]
74
+ #
75
+ # @since 0.2.0
76
+ #
77
+ # @see handled_exceptions=
78
+ #
79
+ # @example
80
+ # configuration.handle_exceptions(ArgumentError => 400}
81
+ def handle_exception(exceptions)
82
+ self.handled_exceptions = handled_exceptions
83
+ .merge(exceptions)
84
+ .sort { |(ex1, _), (ex2, _)| ex1.ancestors.include?(ex2) ? -1 : 1 }
85
+ .to_h
86
+ end
87
+
88
+ # Default MIME type to format mapping
89
+ #
90
+ # @since 0.2.0
91
+ # @api private
92
+ DEFAULT_FORMATS = {
93
+ 'application/octet-stream' => :all,
94
+ '*/*' => :all,
95
+ 'text/html' => :html
96
+ }.freeze
97
+
98
+ # @!method formats=(formats)
99
+ #
100
+ # Specifies the MIME type to format mapping
101
+ #
102
+ # @param formats [Hash{String=>Symbol}] MIME type strings as keys and
103
+ # format symbols as values
104
+ #
105
+ # @return [void]
106
+ #
107
+ # @since 0.2.0
108
+ #
109
+ # @see format
110
+ # @see Hanami::Action::Mime
111
+ #
112
+ # @example
113
+ # configuration.formats = {"text/html" => :html}
114
+ #
115
+ # @!method formats
116
+ #
117
+ # Returns the configured MIME type to format mapping
118
+ #
119
+ # @return [Symbol,nil] the corresponding format, if present
120
+ #
121
+ # @see format
122
+ # @see formats=
123
+ #
124
+ # @since 0.2.0
125
+ setting :formats, default: DEFAULT_FORMATS.dup
126
+
127
+ # Registers a MIME type to format mapping
128
+ #
129
+ # @param hash [Hash{Symbol=>String}] format symbols as keys and the MIME
130
+ # type strings must as values
131
+ #
132
+ # @return [void]
133
+ #
134
+ # @since 0.2.0
135
+ #
136
+ # @see Hanami::Action::Mime
137
+ #
138
+ # @example configuration.format html: "text/html"
139
+ def format(hash)
140
+ symbol, mime_type = *Utils::Kernel.Array(hash)
141
+ formats[Utils::Kernel.String(mime_type)] = Utils::Kernel.Symbol(symbol)
142
+ end
143
+
144
+ # Returns the configured format for the given MIME type
145
+ #
146
+ # @param mime_type [#to_s,#to_str] A mime type
147
+ #
148
+ # @return [Symbol,nil] the corresponding format, nil if not found
149
+ #
150
+ # @see format
151
+ #
152
+ # @since 0.2.0
153
+ # @api private
154
+ def format_for(mime_type)
155
+ formats[mime_type]
156
+ end
157
+
158
+ # Returns the configured format's MIME types
159
+ #
160
+ # @return [Array<String>] the format's MIME types
161
+ #
162
+ # @see formats=
163
+ # @see format
164
+ #
165
+ # @since 0.8.0
166
+ #
167
+ # @api private
168
+ def mime_types
169
+ # FIXME: this isn't efficient. speed it up!
170
+ ((formats.keys - DEFAULT_FORMATS.keys) +
171
+ Hanami::Action::Mime::TYPES.values).freeze
172
+ end
173
+
174
+ # Returns a MIME type for the given format
175
+ #
176
+ # @param format [#to_sym] a format
177
+ #
178
+ # @return [String,nil] the corresponding MIME type, if present
179
+ #
180
+ # @since 0.2.0
181
+ # @api private
182
+ def mime_type_for(format)
183
+ formats.key(format)
184
+ end
185
+
186
+ # @!method default_request_format=(format)
187
+ #
188
+ # Sets a format as default fallback for all the requests without a strict
189
+ # requirement for the MIME type.
190
+ #
191
+ # The given format must be coercible to a symbol, and be a valid MIME
192
+ # type alias. If it isn't, at runtime the framework will raise an
193
+ # `Hanami::Controller::UnknownFormatError`.
194
+ #
195
+ # By default, this value is nil.
196
+ #
197
+ # @param format [Symbol]
198
+ #
199
+ # @return [void]
200
+ #
201
+ # @since 0.5.0
202
+ #
203
+ # @see Hanami::Action::Mime
204
+ #
205
+ # @!method default_request_format
206
+ #
207
+ # Returns the configured default request format
208
+ #
209
+ # @return [Symbol] format
210
+ #
211
+ # @see default_request_format=
212
+ #
213
+ # @since 0.5.0
214
+ setting :default_request_format, constructor: -> format {
215
+ Utils::Kernel.Symbol(format) unless format.nil?
216
+ }
217
+
218
+ # @!method default_response_format=(format)
219
+ #
220
+ # Sets a format to be used for all responses regardless of the request
221
+ # type.
222
+ #
223
+ # The given format must be coercible to a symbol, and be a valid MIME
224
+ # type alias. If it isn't, at the runtime the framework will raise an
225
+ # `Hanami::Controller::UnknownFormatError`.
226
+ #
227
+ # By default, this value is nil.
228
+ #
229
+ # @param format [Symbol]
230
+ #
231
+ # @return [void]
232
+ #
233
+ # @since 0.5.0
234
+ #
235
+ # @see Hanami::Action::Mime
236
+ #
237
+ # @!method default_response_format
238
+ #
239
+ # Returns the configured default response format
240
+ #
241
+ # @return [Symbol] format
242
+ #
243
+ # @see default_request_format=
244
+ #
245
+ # @since 0.5.0
246
+ setting :default_response_format, constructor: -> format {
247
+ Utils::Kernel.Symbol(format) unless format.nil?
248
+ }
249
+
250
+ # @!method default_charset=(charset)
251
+ #
252
+ # Sets a charset (character set) as default fallback for all the requests
253
+ # without a strict requirement for the charset.
254
+ #
255
+ # By default, this value is nil.
256
+ #
257
+ # @param charset [String]
258
+ #
259
+ # @return [void]
260
+ #
261
+ # @since 0.3.0
262
+ #
263
+ # @see Hanami::Action::Mime
264
+ #
265
+ # @!method default_charset
266
+ #
267
+ # Returns the configured default charset.
268
+ #
269
+ # @return [String,nil] the charset, if present
270
+ #
271
+ # @see default_charset=
272
+ #
273
+ # @since 0.3.0
274
+ setting :default_charset
275
+
276
+ # @!method default_headers=(headers)
277
+ #
278
+ # Sets default headers for all responses.
279
+ #
280
+ # By default, this is an empty hash.
281
+ #
282
+ # @param headers [Hash{String=>String}] the headers
283
+ #
284
+ # @return [void]
285
+ #
286
+ # @since 0.4.0
287
+ #
288
+ # @see default_headers
289
+ #
290
+ # @example
291
+ # configuration.default_headers = {'X-Frame-Options' => 'DENY'}
292
+ #
293
+ # @!method default_headers
294
+ #
295
+ # Returns the configured headers
296
+ #
297
+ # @return [Hash{String=>String}] the headers
298
+ #
299
+ # @since 0.4.0
300
+ #
301
+ # @see default_headers=
302
+ setting :default_headers, default: {}, constructor: -> headers { headers.compact }
303
+
304
+ # @!method cookies=(cookie_options)
305
+ #
306
+ # Sets default cookie options for all responses.
307
+ #
308
+ # By default this, is an empty hash.
309
+ #
310
+ # @param cookie_options [Hash{Symbol=>String}] the cookie options
311
+ #
312
+ # @return [void]
313
+ #
314
+ # @since 0.4.0
315
+ #
316
+ # @example
317
+ # configuration.cookies = {
318
+ # domain: 'hanamirb.org',
319
+ # path: '/controller',
320
+ # secure: true,
321
+ # httponly: true
322
+ # }
323
+ #
324
+ # @!method cookies
325
+ #
326
+ # Returns the configured cookie options
327
+ #
328
+ # @return [Hash{Symbol=>String}]
329
+ #
330
+ # @since 0.4.0
331
+ #
332
+ # @see cookies=
333
+ setting :cookies, default: {}, constructor: -> cookie_options {
334
+ # Call `to_h` here to permit `ApplicationConfiguration::Cookies` object to be
335
+ # provided when application actions are configured
336
+ cookie_options.to_h.compact
337
+ }
338
+
339
+ # @!method root_directory=(dir)
340
+ #
341
+ # Sets the the for the public directory, which is used for file downloads.
342
+ # This must be an existent directory.
343
+ #
344
+ # Defaults to the current working directory.
345
+ #
346
+ # @param dir [String] the directory path
347
+ #
348
+ # @return [void]
349
+ #
350
+ # @since 1.0.0
351
+ #
352
+ # @api private
353
+ #
354
+ # @!method root_directory
355
+ #
356
+ # Returns the configured root directory
357
+ #
358
+ # @return [String] the directory path
359
+ #
360
+ # @see root_directory=
361
+ #
362
+ # @since 1.0.0
363
+ #
364
+ # @api private
365
+ setting :root_directory, constructor: -> dir {
366
+ dir ||= Dir.pwd
367
+
368
+ Pathname(dir).realpath
369
+ }
370
+
371
+ # Default public directory
372
+ #
373
+ # This serves as the root directory for file downloads
374
+ #
375
+ # @since 1.0.0
376
+ #
377
+ # @api private
378
+ DEFAULT_PUBLIC_DIRECTORY = 'public'.freeze
379
+
380
+ # @!method public_directory=(directory)
381
+ #
382
+ # Sets the path to public directory. This directory is used for file downloads.
383
+ #
384
+ # This given directory will be appended onto the root directory.
385
+ #
386
+ # By default, the public directory is "public".
387
+ #
388
+ # @param directory [String] the public directory path
389
+ #
390
+ # @return [void]
391
+ #
392
+ # @see root_directory
393
+ # @see public_directory
394
+ setting :public_directory, default: DEFAULT_PUBLIC_DIRECTORY
395
+
396
+ # Returns the configured public directory, appended onto the root directory.
397
+ #
398
+ # @return [String] the fill directory path
399
+ #
400
+ # @example
401
+ # configuration.public_directory = "public"
402
+ #
403
+ # configuration.public_directory
404
+ # # => "/path/to/root/public"
405
+ #
406
+ # @see public_directory=
407
+ # @see root_directory=
408
+ def public_directory
409
+ # This must be a string, for Rack compatibility
410
+ root_directory.join(super).to_s
411
+ end
412
+
413
+ private
414
+
415
+ def method_missing(name, *args, &block)
416
+ if config.respond_to?(name)
417
+ config.public_send(name, *args, &block)
418
+ else
419
+ super
420
+ end
421
+ end
422
+
423
+ def respond_to_missing?(name, _incude_all = false)
424
+ config.respond_to?(name) || super
425
+ end
426
+ end
427
+ end
428
+ end
@@ -1,7 +1,7 @@
1
1
  require 'hanami/utils/hash'
2
2
 
3
3
  module Hanami
4
- module Action
4
+ class Action
5
5
  # A set of HTTP Cookies
6
6
  #
7
7
  # It acts as an Hash
@@ -55,7 +55,7 @@ module Hanami
55
55
  # @since 0.1.0
56
56
  def initialize(env, headers, default_options)
57
57
  @_headers = headers
58
- @cookies = Utils::Hash.new(extract(env)).deep_symbolize!
58
+ @cookies = Utils::Hash.deep_symbolize(extract(env))
59
59
  @default_options = default_options
60
60
  end
61
61
 
@@ -166,7 +166,7 @@ module Hanami
166
166
  # @since 0.4.0
167
167
  # @api private
168
168
  def _merge_default_values(value)
169
- cookies_options = if value.is_a? Hash
169
+ cookies_options = if value.is_a?(::Hash)
170
170
  value.merge! _add_expires_option(value)
171
171
  else
172
172
  { value: value }
@@ -1,7 +1,5 @@
1
- require 'hanami/action/cookie_jar'
2
-
3
1
  module Hanami
4
- module Action
2
+ class Action
5
3
  # Cookies API
6
4
  #
7
5
  # This module isn't included by default.
@@ -10,63 +8,6 @@ module Hanami
10
8
  #
11
9
  # @see Hanami::Action::Cookies#cookies
12
10
  module Cookies
13
- protected
14
-
15
- # Gets the cookies from the request and expose them as an Hash
16
- #
17
- # It automatically sets options from global configuration, but it allows to
18
- # override values case by case.
19
- #
20
- # For a list of options please have a look at <tt>Hanami::Controller::Configuration</tt>,
21
- # and <tt>Hanami::Action::CookieJar</tt>.
22
- #
23
- # @return [Hanami::Action::CookieJar] cookies
24
- #
25
- # @since 0.1.0
26
- #
27
- # @see Hanami::Controller::Configuration#cookies
28
- # @see Hanami::Action::CookieJar#[]=
29
- #
30
- # @example Basic Usage
31
- # require 'hanami/controller'
32
- # require 'hanami/action/cookies'
33
- #
34
- # class Show
35
- # include Hanami::Action
36
- # include Hanami::Action::Cookies
37
- #
38
- # def call(params)
39
- # # ...
40
- #
41
- # # get a value
42
- # cookies[:user_id] # => '23'
43
- #
44
- # # set a value
45
- # cookies[:foo] = 'bar'
46
- #
47
- # # remove a value
48
- # cookies[:bax] = nil
49
- # end
50
- # end
51
- #
52
- # @example Cookies Options
53
- # require 'hanami/controller'
54
- # require 'hanami/action/cookies'
55
- #
56
- # class Show
57
- # include Hanami::Action
58
- # include Hanami::Action::Cookies
59
- #
60
- # def call(params)
61
- # # ...
62
- # # set a value
63
- # cookies[:foo] = { value: 'bar', max_age: 300, path: '/dashboard' }
64
- # end
65
- # end
66
- def cookies
67
- @cookies ||= CookieJar.new(@_env.dup, headers, configuration.cookies)
68
- end
69
-
70
11
  private
71
12
 
72
13
  # Finalize the response by flushing cookies into the response
@@ -75,9 +16,9 @@ module Hanami
75
16
  # @api private
76
17
  #
77
18
  # @see Hanami::Action#finish
78
- def finish
19
+ def finish(req, res, *)
20
+ res.cookies.finish
79
21
  super
80
- cookies.finish
81
22
  end
82
23
  end
83
24
  end