hanami-controller 1.3.2 → 2.0.0.alpha3

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +83 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +299 -537
  5. data/hanami-controller.gemspec +5 -4
  6. data/lib/hanami/action/application_action.rb +112 -0
  7. data/lib/hanami/action/application_configuration/cookies.rb +29 -0
  8. data/lib/hanami/action/application_configuration/sessions.rb +46 -0
  9. data/lib/hanami/action/application_configuration.rb +92 -0
  10. data/lib/hanami/action/base_params.rb +2 -2
  11. data/lib/hanami/action/cache/cache_control.rb +4 -4
  12. data/lib/hanami/action/cache/conditional_get.rb +3 -1
  13. data/lib/hanami/action/cache/directives.rb +1 -1
  14. data/lib/hanami/action/cache/expires.rb +3 -3
  15. data/lib/hanami/action/cache.rb +1 -139
  16. data/lib/hanami/action/configuration.rb +428 -0
  17. data/lib/hanami/action/cookie_jar.rb +3 -3
  18. data/lib/hanami/action/cookies.rb +3 -62
  19. data/lib/hanami/action/csrf_protection.rb +214 -0
  20. data/lib/hanami/action/flash.rb +102 -207
  21. data/lib/hanami/action/glue.rb +5 -31
  22. data/lib/hanami/action/halt.rb +12 -0
  23. data/lib/hanami/action/mime.rb +78 -485
  24. data/lib/hanami/action/params.rb +2 -2
  25. data/lib/hanami/action/rack/file.rb +1 -1
  26. data/lib/hanami/action/request.rb +30 -20
  27. data/lib/hanami/action/response.rb +193 -0
  28. data/lib/hanami/action/session.rb +11 -128
  29. data/lib/hanami/action/standalone_action.rb +579 -0
  30. data/lib/hanami/action/validatable.rb +1 -1
  31. data/lib/hanami/action/view_name_inferrer.rb +46 -0
  32. data/lib/hanami/action.rb +129 -73
  33. data/lib/hanami/controller/version.rb +1 -1
  34. data/lib/hanami/controller.rb +0 -227
  35. data/lib/hanami/http/status.rb +2 -2
  36. metadata +45 -27
  37. data/lib/hanami/action/callable.rb +0 -92
  38. data/lib/hanami/action/callbacks.rb +0 -214
  39. data/lib/hanami/action/configurable.rb +0 -50
  40. data/lib/hanami/action/exposable/guard.rb +0 -104
  41. data/lib/hanami/action/exposable.rb +0 -126
  42. data/lib/hanami/action/head.rb +0 -121
  43. data/lib/hanami/action/rack/callable.rb +0 -47
  44. data/lib/hanami/action/rack.rb +0 -399
  45. data/lib/hanami/action/redirect.rb +0 -59
  46. data/lib/hanami/action/throwable.rb +0 -196
  47. data/lib/hanami/controller/configuration.rb +0 -763
  48. 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