hanami-controller 1.3.3 → 2.0.0.alpha4

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 (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
@@ -1,47 +0,0 @@
1
- module Hanami
2
- module Action
3
- module Rack
4
- module Callable
5
- # Callable module for actions. With this module, actions with middlewares
6
- # will be able to work with rack builder.
7
- #
8
- # @param env [Hash] the full Rack env or the params. This value may vary,
9
- # see the examples below.
10
- #
11
- # @since 0.4.0
12
- # @api private
13
- #
14
- # @see Hanami::Action::Rack::ClassMethods#rack_builder
15
- # @see Hanami::Action::Rack::ClassMethods#use
16
- #
17
- # @example
18
- # require 'hanami/controller'
19
- #
20
- # class MyMiddleware
21
- # def initialize(app)
22
- # @app = app
23
- # end
24
- #
25
- # def call(env)
26
- # #...
27
- # end
28
- # end
29
- #
30
- # class Show
31
- # include Hanami::Action
32
- # use MyMiddleware
33
- #
34
- # def call(params)
35
- # # ...
36
- # puts params # => { id: 23 } extracted from Rack env
37
- # end
38
- # end
39
- #
40
- # Show.respond_to?(:call) # => true
41
- def call(env)
42
- rack_builder.call(env)
43
- end
44
- end
45
- end
46
- end
47
- end
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Hanami
4
- module Action
5
- module Rack
6
- # This module provides method to set exceptions to Rack env:
7
- #
8
- # * `rack.errors` - IO for errors, as requested by Rack SPEC
9
- # * `rack.exception` - De-facto standard for Ruby exception tracking SaaS
10
- #
11
- # @see http://www.rubydoc.info/github/rack/rack/file/SPEC#The_Error_Stream
12
- # @see https://github.com/hanami/controller/issues/133
13
- #
14
- # @since 1.3.3
15
- # @api private
16
- module Errors
17
- # @since 1.3.3
18
- # @api private
19
- RACK_ERRORS = "rack.errors"
20
-
21
- # @since 1.3.3
22
- # @api private
23
- RACK_EXCEPTION = "rack.exception"
24
-
25
- # Set exception in Rack env
26
- #
27
- # @param env [Hash] the Rack environment
28
- # @param exception [Exception] the exception to set
29
- #
30
- # @since 1.3.3
31
- # @api private
32
- def self.set(env, exception)
33
- env[RACK_EXCEPTION] = exception
34
-
35
- return unless errors = env[RACK_ERRORS] # rubocop:disable Lint/AssignmentInCondition
36
-
37
- errors.write(_dump_exception(exception))
38
- errors.flush
39
- end
40
-
41
- # Format exception info with name and backtrace
42
- #
43
- # @param exception [Exception]
44
- #
45
- # @since 1.3.3
46
- # @api private
47
- def self._dump_exception(exception)
48
- [[exception.class, exception.message].compact.join(": "), *exception.backtrace].join("\n\t")
49
- end
50
- end
51
- end
52
- end
53
- end
@@ -1,411 +0,0 @@
1
- require 'securerandom'
2
- require 'hanami/action/request'
3
- require 'hanami/action/base_params'
4
- require 'hanami/action/rack/callable'
5
- require 'hanami/action/rack/file'
6
- require 'hanami/utils/deprecation'
7
-
8
- module Hanami
9
- module Action
10
- # Rack integration API
11
- #
12
- # @since 0.1.0
13
- module Rack
14
- # Rack SPEC response code
15
- #
16
- # @since 1.0.0
17
- # @api private
18
- RESPONSE_CODE = 0
19
-
20
- # Rack SPEC response headers
21
- #
22
- # @since 1.0.0
23
- # @api private
24
- RESPONSE_HEADERS = 1
25
-
26
- # Rack SPEC response body
27
- #
28
- # @since 1.0.0
29
- # @api private
30
- RESPONSE_BODY = 2
31
-
32
- # The default HTTP response code
33
- #
34
- # @since 0.1.0
35
- # @api private
36
- DEFAULT_RESPONSE_CODE = 200
37
-
38
- # Not Found
39
- #
40
- # @since 1.0.0
41
- # @api private
42
- NOT_FOUND = 404
43
-
44
- # The default Rack response body
45
- #
46
- # @since 0.1.0
47
- # @api private
48
- DEFAULT_RESPONSE_BODY = []
49
-
50
- # The default HTTP Request ID length
51
- #
52
- # @since 0.3.0
53
- # @api private
54
- #
55
- # @see Hanami::Action::Rack#request_id
56
- DEFAULT_REQUEST_ID_LENGTH = 16
57
-
58
- # The request method
59
- #
60
- # @since 0.3.2
61
- # @api private
62
- REQUEST_METHOD = 'REQUEST_METHOD'.freeze
63
-
64
- # The Content-Length HTTP header
65
- #
66
- # @since 1.0.0
67
- # @api private
68
- CONTENT_LENGTH = 'Content-Length'.freeze
69
-
70
- # The non-standard HTTP header to pass the control over when a resource
71
- # cannot be found by the current endpoint
72
- #
73
- # @since 1.0.0
74
- # @api private
75
- X_CASCADE = 'X-Cascade'.freeze
76
-
77
- # HEAD request
78
- #
79
- # @since 0.3.2
80
- # @api private
81
- HEAD = 'HEAD'.freeze
82
-
83
- # The key that returns router parsed body from the Rack env
84
- ROUTER_PARSED_BODY = 'router.parsed_body'.freeze
85
-
86
- # This is the root directory for `#unsafe_send_file`
87
- #
88
- # @since 1.3.3
89
- # @api private
90
- #
91
- # @see #unsafe_send_file
92
- FILE_SYSTEM_ROOT = Pathname.new("/").freeze
93
-
94
- # Override Ruby's hook for modules.
95
- # It includes basic Hanami::Action modules to the given class.
96
- #
97
- # @param base [Class] the target action
98
- #
99
- # @since 0.1.0
100
- # @api private
101
- #
102
- # @see http://www.ruby-doc.org/core-2.1.2/Module.html#method-i-included
103
- def self.included(base)
104
- base.class_eval do
105
- extend ClassMethods
106
- prepend InstanceMethods
107
- end
108
- end
109
-
110
- # @api private
111
- module ClassMethods
112
- # Build rack builder
113
- #
114
- # @return [Rack::Builder]
115
- # @api private
116
- def rack_builder
117
- @rack_builder ||= begin
118
- extend Hanami::Action::Rack::Callable
119
- rack_builder = ::Rack::Builder.new
120
- rack_builder.run ->(env) { self.new.call(env) }
121
- rack_builder
122
- end
123
- end
124
-
125
- # Use a Rack middleware
126
- #
127
- # The middleware will be used as it is.
128
- #
129
- # At the runtime, the middleware be invoked with the raw Rack env.
130
- #
131
- # Multiple middlewares can be employed, just by using multiple times
132
- # this method.
133
- #
134
- # @param middleware [#call] A Rack middleware
135
- # @param args [Array] Array arguments for middleware
136
- #
137
- # @since 0.2.0
138
- #
139
- # @see Hanami::Action::Callbacks::ClassMethods#before
140
- #
141
- # @example Middleware
142
- # require 'hanami/controller'
143
- #
144
- # module Sessions
145
- # class Create
146
- # include Hanami::Action
147
- # use OmniAuth
148
- #
149
- # def call(params)
150
- # # ...
151
- # end
152
- # end
153
- # end
154
- def use(middleware, *args, &block)
155
- rack_builder.use middleware, *args, &block
156
- end
157
-
158
- # Returns the class which defines the params
159
- #
160
- # Returns the class which has been provided to define the
161
- # params. By default this will be Hanami::Action::Params.
162
- #
163
- # @return [Class] A params class (when whitelisted) or
164
- # Hanami::Action::Params
165
- #
166
- # @api private
167
- # @since 0.7.0
168
- def params_class
169
- @params_class ||= BaseParams
170
- end
171
- end
172
-
173
- # @since 0.7.0
174
- # @api private
175
- module InstanceMethods
176
- # @since 0.7.0
177
- # @api private
178
- def initialize(*)
179
- super
180
- @_status = nil
181
- @_body = nil
182
- end
183
- end
184
-
185
- protected
186
- # Gets the headers from the response
187
- #
188
- # @return [Hash] the HTTP headers from the response
189
- #
190
- # @since 0.1.0
191
- #
192
- # @example
193
- # require 'hanami/controller'
194
- #
195
- # class Show
196
- # include Hanami::Action
197
- #
198
- # def call(params)
199
- # # ...
200
- # self.headers # => { ... }
201
- # self.headers.merge!({'X-Custom' => 'OK'})
202
- # end
203
- # end
204
- def headers
205
- @headers
206
- end
207
-
208
- # Returns a serialized Rack response (Array), according to the current
209
- # status code, headers, and body.
210
- #
211
- # @return [Array] the serialized response
212
- #
213
- # @since 0.1.0
214
- # @api private
215
- #
216
- # @see Hanami::Action::Rack::DEFAULT_RESPONSE_CODE
217
- # @see Hanami::Action::Rack::DEFAULT_RESPONSE_BODY
218
- # @see Hanami::Action::Rack#status=
219
- # @see Hanami::Action::Rack#headers
220
- # @see Hanami::Action::Rack#body=
221
- def response
222
- [ @_status || DEFAULT_RESPONSE_CODE, headers, @_body || DEFAULT_RESPONSE_BODY.dup ]
223
- end
224
-
225
- # Calculates an unique ID for the current request
226
- #
227
- # @return [String] The unique ID
228
- #
229
- # @since 0.3.0
230
- def request_id
231
- # FIXME make this number configurable and document the probabilities of clashes
232
- @request_id ||= SecureRandom.hex(DEFAULT_REQUEST_ID_LENGTH)
233
- end
234
-
235
- # Returns a Hanami specialized rack request
236
- #
237
- # @return [Hanami::Action::Request] The request
238
- #
239
- # @since 0.3.1
240
- #
241
- # @example
242
- # require 'hanami/controller'
243
- #
244
- # class Create
245
- # include Hanami::Action
246
- #
247
- # def call(params)
248
- # ip = request.ip
249
- # secure = request.ssl?
250
- # end
251
- # end
252
- def request
253
- @request ||= ::Hanami::Action::Request.new(@_env)
254
- end
255
-
256
- # Return parsed request body
257
- #
258
- # @deprecated
259
- def parsed_request_body
260
- Hanami::Utils::Deprecation.new('#parsed_request_body is deprecated and it will be removed in future versions')
261
- @_env.fetch(ROUTER_PARSED_BODY, nil)
262
- end
263
-
264
- private
265
-
266
- # Sets the HTTP status code for the response
267
- #
268
- # @param status [Fixnum] an HTTP status code
269
- # @return [void]
270
- #
271
- # @since 0.1.0
272
- #
273
- # @example
274
- # require 'hanami/controller'
275
- #
276
- # class Create
277
- # include Hanami::Action
278
- #
279
- # def call(params)
280
- # # ...
281
- # self.status = 201
282
- # end
283
- # end
284
- def status=(status)
285
- @_status = status
286
- end
287
-
288
- # Sets the body of the response
289
- #
290
- # @param body [String] the body of the response
291
- # @return [void]
292
- #
293
- # @since 0.1.0
294
- #
295
- # @example
296
- # require 'hanami/controller'
297
- #
298
- # class Show
299
- # include Hanami::Action
300
- #
301
- # def call(params)
302
- # # ...
303
- # self.body = 'Hi!'
304
- # end
305
- # end
306
- def body=(body)
307
- body = Array(body) unless body.respond_to?(:each)
308
- @_body = body
309
- end
310
-
311
- # Send a file as response.
312
- # <tt>This method only sends files from the public directory</tt>
313
- #
314
- # It automatically handle the following cases:
315
- #
316
- # * <tt>Content-Type</tt> and <tt>Content-Length</tt>
317
- # * File Not found (returns a 404)
318
- # * Conditional GET (via <tt>If-Modified-Since</tt> header)
319
- # * Range requests (via <tt>Range</tt> header)
320
- #
321
- # @param path [String, Pathname] the body of the response
322
- # @return [void]
323
- #
324
- # @since 0.4.3
325
- #
326
- # @example
327
- # require 'hanami/controller'
328
- #
329
- # class Show
330
- # include Hanami::Action
331
- #
332
- # def call(params)
333
- # # ...
334
- # send_file Pathname.new('path/to/file')
335
- # end
336
- # end
337
- def send_file(path)
338
- _send_file(
339
- File.new(path, self.class.configuration.public_directory).call(@_env)
340
- )
341
- end
342
-
343
- # Send a file as response from anywhere in the file system.
344
- #
345
- # @see Hanami::Action::Rack#send_file
346
- #
347
- # @param path [String, Pathname] path to the file to be sent
348
- # @return [void]
349
- #
350
- # @since 1.0.0
351
- #
352
- # @example
353
- # require 'hanami/controller'
354
- #
355
- # class Show
356
- # include Hanami::Action
357
- #
358
- # def call(params)
359
- # # ...
360
- # unsafe_send_file Pathname.new('/tmp/path/to/file')
361
- # end
362
- # end
363
- def unsafe_send_file(path)
364
- directory = if Pathname.new(path).relative?
365
- self.class.configuration.root_directory
366
- else
367
- FILE_SYSTEM_ROOT
368
- end
369
-
370
- _send_file(
371
- File.new(path, directory).call(@_env)
372
- )
373
- end
374
-
375
- # Check if the current request is a HEAD
376
- #
377
- # @return [TrueClass,FalseClass] the result of the check
378
- #
379
- # @since 0.3.2
380
- def head?
381
- request_method == HEAD
382
- end
383
-
384
- # NOTE: <tt>Hanami::Action::CSRFProtection</tt> (<tt>hanamirb</tt> gem) depends on this.
385
- #
386
- # @api private
387
- # @since 0.4.4
388
- def request_method
389
- @_env[REQUEST_METHOD]
390
- end
391
-
392
- # @since 1.0.0
393
- # @api private
394
- def _send_file(response)
395
- headers.merge!(response[RESPONSE_HEADERS])
396
-
397
- if response[RESPONSE_CODE] == NOT_FOUND
398
- headers.delete(X_CASCADE)
399
- headers.delete(CONTENT_LENGTH)
400
- halt NOT_FOUND
401
- else
402
- # FIXME: this is a fix for https://github.com/hanami/controller/issues/240
403
- # It's here to maintain the backward compat with 1.1, as we can't remove `#halt`
404
- # We should review the workflow for 2.0, because I don't like callbacks to be referenced from here.
405
- _run_after_callbacks(params)
406
- halt response[RESPONSE_CODE], response[RESPONSE_BODY]
407
- end
408
- end
409
- end
410
- end
411
- end
@@ -1,59 +0,0 @@
1
- module Hanami
2
- module Action
3
- # HTTP redirect API
4
- #
5
- # @since 0.1.0
6
- module Redirect
7
- # The HTTP header for redirects
8
- #
9
- # @since 0.2.0
10
- # @api private
11
- LOCATION = 'Location'.freeze
12
-
13
- private
14
-
15
- # Redirect to the given URL and halt the request
16
- #
17
- # @param url [String] the destination URL
18
- # @param status [Fixnum] the http code
19
- #
20
- # @since 0.1.0
21
- #
22
- # @see Hanami::Action::Throwable#halt
23
- #
24
- # @example With default status code (302)
25
- # require 'hanami/controller'
26
- #
27
- # class Create
28
- # include Hanami::Action
29
- #
30
- # def call(params)
31
- # # ...
32
- # redirect_to 'http://example.com/articles/23'
33
- # end
34
- # end
35
- #
36
- # action = Create.new
37
- # action.call({}) # => [302, {'Location' => '/articles/23'}, '']
38
- #
39
- # @example With custom status code
40
- # require 'hanami/controller'
41
- #
42
- # class Create
43
- # include Hanami::Action
44
- #
45
- # def call(params)
46
- # # ...
47
- # redirect_to 'http://example.com/articles/23', status: 301
48
- # end
49
- # end
50
- #
51
- # action = Create.new
52
- # action.call({}) # => [301, {'Location' => '/articles/23'}, '']
53
- def redirect_to(url, status: 302)
54
- headers[LOCATION] = ::String.new(url)
55
- halt(status)
56
- end
57
- end
58
- end
59
- end