hanami-controller 2.0.0.alpha8 → 2.0.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.
@@ -1,4 +1,6 @@
1
- require 'hanami/action/cache/directives'
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/action/cache/directives"
2
4
 
3
5
  module Hanami
4
6
  class Action
@@ -8,12 +10,6 @@ module Hanami
8
10
  # @since 0.3.0
9
11
  # @api private
10
12
  module CacheControl
11
- # The HTTP header for Cache-Control
12
- #
13
- # @since 0.3.0
14
- # @api private
15
- HEADER = 'Cache-Control'.freeze
16
-
17
13
  # @since 0.3.0
18
14
  # @api private
19
15
  def self.included(base)
@@ -37,7 +33,7 @@ module Hanami
37
33
  def cache_control_directives
38
34
  @cache_control_directives || Object.new.tap do |null_object|
39
35
  def null_object.headers
40
- ::Hash.new
36
+ {}
41
37
  end
42
38
  end
43
39
  end
@@ -50,7 +46,10 @@ module Hanami
50
46
  #
51
47
  # @see Hanami::Action#finish
52
48
  def finish(_, res, _)
53
- res.headers.merge!(self.class.cache_control_directives.headers) unless res.headers.include? HEADER
49
+ unless res.headers.include?(Action::CACHE_CONTROL)
50
+ res.headers.merge!(self.class.cache_control_directives.headers)
51
+ end
52
+
54
53
  super
55
54
  end
56
55
 
@@ -59,6 +58,11 @@ module Hanami
59
58
  # @since 0.3.0
60
59
  # @api private
61
60
  class Directives
61
+ # @since 2.0.0
62
+ # @api private
63
+ SEPARATOR = ", "
64
+ private_constant :SEPARATOR
65
+
62
66
  # @since 0.3.0
63
67
  # @api private
64
68
  def initialize(*values)
@@ -69,7 +73,7 @@ module Hanami
69
73
  # @api private
70
74
  def headers
71
75
  if @directives.any?
72
- { HEADER => @directives.join(', ') }
76
+ {Action::CACHE_CONTROL => @directives.join(SEPARATOR)}
73
77
  else
74
78
  {}
75
79
  end
@@ -1,28 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "hanami/utils/blank"
2
4
 
3
5
  module Hanami
4
6
  class Action
5
7
  module Cache
6
- # @since 0.3.0
7
- # @api private
8
- IF_NONE_MATCH = 'HTTP_IF_NONE_MATCH'.freeze
9
-
10
- # The HTTP header for ETag
11
- #
12
- # @since 0.3.0
13
- # @api private
14
- ETAG = 'ETag'.freeze
15
-
16
- # @since 0.3.0
17
- # @api private
18
- IF_MODIFIED_SINCE = 'HTTP_IF_MODIFIED_SINCE'.freeze
19
-
20
- # The HTTP header for Last-Modified
21
- #
22
- # @since 0.3.0
23
- # @api private
24
- LAST_MODIFIED = 'Last-Modified'.freeze
25
-
26
8
  # ETag value object
27
9
  #
28
10
  # @since 0.3.0
@@ -43,7 +25,7 @@ module Hanami
43
25
  # @since 0.3.0
44
26
  # @api private
45
27
  def header
46
- { ETAG => @value } if @value
28
+ {Action::ETAG => @value} if @value
47
29
  end
48
30
 
49
31
  private
@@ -51,7 +33,7 @@ module Hanami
51
33
  # @since 0.3.0
52
34
  # @api private
53
35
  def none_match
54
- @env[IF_NONE_MATCH]
36
+ @env[Action::IF_NONE_MATCH]
55
37
  end
56
38
  end
57
39
 
@@ -78,7 +60,7 @@ module Hanami
78
60
  # @since 0.3.0
79
61
  # @api private
80
62
  def header
81
- { LAST_MODIFIED => @value.httpdate } if @value && @value.respond_to?(:httpdate)
63
+ {Action::LAST_MODIFIED => @value.httpdate} if @value.respond_to?(:httpdate)
82
64
  end
83
65
 
84
66
  private
@@ -86,7 +68,7 @@ module Hanami
86
68
  # @since 0.3.0
87
69
  # @api private
88
70
  def modified_since
89
- @env[IF_MODIFIED_SINCE]
71
+ @env[Action::IF_MODIFIED_SINCE]
90
72
  end
91
73
  end
92
74
 
@@ -99,7 +81,7 @@ module Hanami
99
81
  # @since 0.3.0
100
82
  # @api private
101
83
  def initialize(env, options)
102
- @validations = [ ETag.new(env, options[:etag]), LastModified.new(env, options[:last_modified]) ]
84
+ @validations = [ETag.new(env, options[:etag]), LastModified.new(env, options[:last_modified])]
103
85
  end
104
86
 
105
87
  # @since 0.3.0
@@ -111,7 +93,7 @@ module Hanami
111
93
  # @since 0.3.0
112
94
  # @api private
113
95
  def headers
114
- @validations.map(&:header).compact.reduce Hash.new, :merge
96
+ @validations.map(&:header).compact.reduce({}, :merge)
115
97
  end
116
98
  end
117
99
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Hanami
2
4
  class Action
3
5
  module Cache
@@ -5,13 +7,13 @@ module Hanami
5
7
  #
6
8
  # @since 0.3.0
7
9
  # @api private
8
- VALUE_DIRECTIVES = %i(max_age s_maxage min_fresh max_stale).freeze
10
+ VALUE_DIRECTIVES = %i[max_age s_maxage min_fresh max_stale].freeze
9
11
 
10
12
  # Cache-Control directives which are implicitly true
11
13
  #
12
14
  # @since 0.3.0
13
15
  # @api private
14
- NON_VALUE_DIRECTIVES = %i(public private no_cache no_store no_transform must_revalidate proxy_revalidate).freeze
16
+ NON_VALUE_DIRECTIVES = %i[public private no_cache no_store no_transform must_revalidate proxy_revalidate].freeze
15
17
 
16
18
  # Class representing value directives
17
19
  #
@@ -63,7 +65,7 @@ module Hanami
63
65
  # @since 0.3.0
64
66
  # @api private
65
67
  def to_str
66
- @name.to_s.tr('_', '-')
68
+ @name.to_s.tr("_", "-")
67
69
  end
68
70
 
69
71
  # @since 0.3.0
@@ -85,7 +87,7 @@ module Hanami
85
87
  def initialize(*values)
86
88
  @directives = []
87
89
  values.each do |directive_key|
88
- if directive_key.kind_of? Hash
90
+ if directive_key.is_a? Hash
89
91
  directive_key.each { |name, value| self.<< ValueDirective.new(name, value) }
90
92
  else
91
93
  self.<< NonValueDirective.new(directive_key)
@@ -95,8 +97,8 @@ module Hanami
95
97
 
96
98
  # @since 0.3.0
97
99
  # @api private
98
- def each
99
- @directives.each { |d| yield d }
100
+ def each(&block)
101
+ @directives.each(&block)
100
102
  end
101
103
 
102
104
  # @since 0.3.0
@@ -1,4 +1,6 @@
1
- require 'hanami/action/cache/cache_control'
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/action/cache/cache_control"
2
4
 
3
5
  module Hanami
4
6
  class Action
@@ -8,12 +10,6 @@ module Hanami
8
10
  # @since 0.3.0
9
11
  # @api private
10
12
  module Expires
11
- # The HTTP header for Expires
12
- #
13
- # @since 0.3.0
14
- # @api private
15
- HEADER = 'Expires'.freeze
16
-
17
13
  # @since 0.3.0
18
14
  # @api private
19
15
  def self.included(base)
@@ -37,7 +33,7 @@ module Hanami
37
33
  def expires_directives
38
34
  @expires_directives || Object.new.tap do |null_object|
39
35
  def null_object.headers
40
- Hash.new
36
+ {}
41
37
  end
42
38
  end
43
39
  end
@@ -50,7 +46,10 @@ module Hanami
50
46
  #
51
47
  # @see Hanami::Action#finish
52
48
  def finish(_, res, _)
53
- res.headers.merge!(self.class.expires_directives.headers) unless res.headers.include? HEADER
49
+ unless res.headers.include?(Action::EXPIRES)
50
+ res.headers.merge!(self.class.expires_directives.headers)
51
+ end
52
+
54
53
  super
55
54
  end
56
55
 
@@ -63,13 +62,13 @@ module Hanami
63
62
  # @api private
64
63
  def initialize(amount, *values)
65
64
  @amount = amount
66
- @cache_control = Hanami::Action::Cache::CacheControl::Directives.new(*(values << { max_age: amount }))
65
+ @cache_control = Hanami::Action::Cache::CacheControl::Directives.new(*(values << {max_age: amount}))
67
66
  end
68
67
 
69
68
  # @since 0.3.0
70
69
  # @api private
71
70
  def headers
72
- { HEADER => time.httpdate }.merge(@cache_control.headers)
71
+ {Action::EXPIRES => time.httpdate}.merge(@cache_control.headers)
73
72
  end
74
73
 
75
74
  private
@@ -1,6 +1,8 @@
1
- require 'hanami/action/cache/cache_control'
2
- require 'hanami/action/cache/expires'
3
- require 'hanami/action/cache/conditional_get'
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/action/cache/cache_control"
4
+ require "hanami/action/cache/expires"
5
+ require "hanami/action/cache/conditional_get"
4
6
 
5
7
  module Hanami
6
8
  class Action
@@ -90,9 +90,9 @@ module Hanami
90
90
  # @since 0.2.0
91
91
  # @api private
92
92
  DEFAULT_FORMATS = {
93
- 'application/octet-stream' => :all,
94
- '*/*' => :all,
95
- 'text/html' => :html
93
+ "application/octet-stream" => :all,
94
+ "*/*" => :all,
95
+ "text/html" => :html
96
96
  }.freeze
97
97
 
98
98
  # @!method formats=(formats)
@@ -211,7 +211,7 @@ module Hanami
211
211
  # @see default_request_format=
212
212
  #
213
213
  # @since 0.5.0
214
- setting :default_request_format, constructor: -> format {
214
+ setting :default_request_format, constructor: -> (format) {
215
215
  Utils::Kernel.Symbol(format) unless format.nil?
216
216
  }
217
217
 
@@ -243,7 +243,7 @@ module Hanami
243
243
  # @see default_request_format=
244
244
  #
245
245
  # @since 0.5.0
246
- setting :default_response_format, constructor: -> format {
246
+ setting :default_response_format, constructor: -> (format) {
247
247
  Utils::Kernel.Symbol(format) unless format.nil?
248
248
  }
249
249
 
@@ -288,7 +288,7 @@ module Hanami
288
288
  # @see default_headers
289
289
  #
290
290
  # @example
291
- # configuration.default_headers = {'X-Frame-Options' => 'DENY'}
291
+ # configuration.default_headers = {"X-Frame-Options" => "DENY"}
292
292
  #
293
293
  # @!method default_headers
294
294
  #
@@ -299,7 +299,7 @@ module Hanami
299
299
  # @since 0.4.0
300
300
  #
301
301
  # @see default_headers=
302
- setting :default_headers, default: {}, constructor: -> headers { headers.compact }
302
+ setting :default_headers, default: {}, constructor: -> (headers) { headers.compact }
303
303
 
304
304
  # @!method cookies=(cookie_options)
305
305
  #
@@ -315,8 +315,8 @@ module Hanami
315
315
  #
316
316
  # @example
317
317
  # configuration.cookies = {
318
- # domain: 'hanamirb.org',
319
- # path: '/controller',
318
+ # domain: "hanamirb.org",
319
+ # path: "/controller",
320
320
  # secure: true,
321
321
  # httponly: true
322
322
  # }
@@ -330,7 +330,7 @@ module Hanami
330
330
  # @since 0.4.0
331
331
  #
332
332
  # @see cookies=
333
- setting :cookies, default: {}, constructor: -> cookie_options {
333
+ setting :cookies, default: {}, constructor: -> (cookie_options) {
334
334
  # Call `to_h` here to permit `ApplicationConfiguration::Cookies` object to be
335
335
  # provided when application actions are configured
336
336
  cookie_options.to_h.compact
@@ -362,7 +362,7 @@ module Hanami
362
362
  # @since 1.0.0
363
363
  #
364
364
  # @api private
365
- setting :root_directory, constructor: -> dir {
365
+ setting :root_directory, constructor: -> (dir) {
366
366
  dir ||= Dir.pwd
367
367
 
368
368
  Pathname(dir).realpath
@@ -375,7 +375,7 @@ module Hanami
375
375
  # @since 1.0.0
376
376
  #
377
377
  # @api private
378
- DEFAULT_PUBLIC_DIRECTORY = 'public'.freeze
378
+ DEFAULT_PUBLIC_DIRECTORY = "public"
379
379
 
380
380
  # @!method public_directory=(directory)
381
381
  #
@@ -389,6 +389,8 @@ module Hanami
389
389
  #
390
390
  # @return [void]
391
391
  #
392
+ # @since 2.0.0
393
+ #
392
394
  # @see root_directory
393
395
  # @see public_directory
394
396
  setting :public_directory, default: DEFAULT_PUBLIC_DIRECTORY
@@ -403,6 +405,8 @@ module Hanami
403
405
  # configuration.public_directory
404
406
  # # => "/path/to/root/public"
405
407
  #
408
+ # @since 2.0.0
409
+ #
406
410
  # @see public_directory=
407
411
  # @see root_directory=
408
412
  def public_directory
@@ -412,6 +416,8 @@ module Hanami
412
416
 
413
417
  private
414
418
 
419
+ # @since 2.0.0
420
+ # @api private
415
421
  def method_missing(name, *args, &block)
416
422
  if config.respond_to?(name)
417
423
  config.public_send(name, *args, &block)
@@ -420,6 +426,8 @@ module Hanami
420
426
  end
421
427
  end
422
428
 
429
+ # @since 2.0.0
430
+ # @api private
423
431
  def respond_to_missing?(name, _incude_all = false)
424
432
  config.respond_to?(name) || super
425
433
  end
@@ -0,0 +1,261 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack"
4
+
5
+ module Hanami
6
+ class Action
7
+ # Rack SPEC response code
8
+ #
9
+ # @since 1.0.0
10
+ # @api private
11
+ RESPONSE_CODE = 0
12
+
13
+ # Rack SPEC response headers
14
+ #
15
+ # @since 1.0.0
16
+ # @api private
17
+ RESPONSE_HEADERS = 1
18
+
19
+ # Rack SPEC response body
20
+ #
21
+ # @since 1.0.0
22
+ # @api private
23
+ RESPONSE_BODY = 2
24
+
25
+ # @since 1.0.0
26
+ # @api private
27
+ DEFAULT_ERROR_CODE = 500
28
+
29
+ # Status codes that by RFC must not include a message body
30
+ #
31
+ # @since 0.3.2
32
+ # @api private
33
+ HTTP_STATUSES_WITHOUT_BODY = Set.new((100..199).to_a << 204 << 205 << 304).freeze
34
+
35
+ # Not Found
36
+ #
37
+ # @since 1.0.0
38
+ # @api private
39
+ NOT_FOUND = 404
40
+
41
+ # Entity headers allowed in blank body responses, according to
42
+ # RFC 2616 - Section 10 (HTTP 1.1).
43
+ #
44
+ # "The response MAY include new or updated metainformation in the form
45
+ # of entity-headers".
46
+ #
47
+ # @since 0.4.0
48
+ # @api private
49
+ #
50
+ # @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.5
51
+ # @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html
52
+ ENTITY_HEADERS = {
53
+ "Allow" => true,
54
+ "Content-Encoding" => true,
55
+ "Content-Language" => true,
56
+ "Content-Location" => true,
57
+ "Content-MD5" => true,
58
+ "Content-Range" => true,
59
+ "Expires" => true,
60
+ "Last-Modified" => true,
61
+ "extension-header" => true
62
+ }.freeze
63
+
64
+ # The request relative path
65
+ #
66
+ # @since 2.0.0
67
+ # @api private
68
+ PATH_INFO = ::Rack::PATH_INFO
69
+
70
+ # The request method
71
+ #
72
+ # @since 0.3.2
73
+ # @api private
74
+ REQUEST_METHOD = ::Rack::REQUEST_METHOD
75
+
76
+ # The Content-Length HTTP header
77
+ #
78
+ # @since 1.0.0
79
+ # @api private
80
+ CONTENT_LENGTH = ::Rack::CONTENT_LENGTH
81
+
82
+ # The non-standard HTTP header to pass the control over when a resource
83
+ # cannot be found by the current endpoint
84
+ #
85
+ # @since 1.0.0
86
+ # @api private
87
+ X_CASCADE = "X-Cascade"
88
+
89
+ # HEAD request
90
+ #
91
+ # @since 0.3.2
92
+ # @api private
93
+ HEAD = ::Rack::HEAD
94
+
95
+ # GET request
96
+ #
97
+ # @since 2.0.0
98
+ # @api private
99
+ GET = ::Rack::GET
100
+
101
+ # TRACE request
102
+ #
103
+ # @since 2.0.0
104
+ # @api private
105
+ TRACE = ::Rack::TRACE
106
+
107
+ # OPTIONS request
108
+ #
109
+ # @since 2.0.0
110
+ # @api private
111
+ OPTIONS = ::Rack::OPTIONS
112
+
113
+ # The key that returns accepted mime types from the Rack env
114
+ #
115
+ # @since 0.1.0
116
+ # @api private
117
+ HTTP_ACCEPT = "HTTP_ACCEPT"
118
+
119
+ # The header key to set the mime type of the response
120
+ #
121
+ # @since 0.1.0
122
+ # @api private
123
+ CONTENT_TYPE = ::Rack::CONTENT_TYPE
124
+
125
+ # The default mime type for an incoming HTTP request
126
+ #
127
+ # @since 0.1.0
128
+ # @api private
129
+ DEFAULT_ACCEPT = "*/*"
130
+
131
+ # The default mime type that is returned in the response
132
+ #
133
+ # @since 0.1.0
134
+ # @api private
135
+ DEFAULT_CONTENT_TYPE = "application/octet-stream"
136
+
137
+ # @since 0.2.0
138
+ # @api private
139
+ RACK_ERRORS = ::Rack::RACK_ERRORS
140
+
141
+ # The HTTP header for Cache-Control
142
+ #
143
+ # @since 2.0.0
144
+ # @api private
145
+ CACHE_CONTROL = ::Rack::CACHE_CONTROL
146
+
147
+ # @since 2.0.0
148
+ # @api private
149
+ IF_NONE_MATCH = "HTTP_IF_NONE_MATCH"
150
+
151
+ # The HTTP header for ETag
152
+ #
153
+ # @since 2.0.0
154
+ # @api private
155
+ ETAG = ::Rack::ETAG
156
+
157
+ # @since 2.0.0
158
+ # @api private
159
+ IF_MODIFIED_SINCE = "HTTP_IF_MODIFIED_SINCE"
160
+
161
+ # The HTTP header for Expires
162
+ #
163
+ # @since 2.0.0
164
+ # @api private
165
+ EXPIRES = ::Rack::EXPIRES
166
+
167
+ # The HTTP header for Last-Modified
168
+ #
169
+ # @since 0.3.0
170
+ # @api private
171
+ LAST_MODIFIED = "Last-Modified"
172
+
173
+ # This isn't part of Rack SPEC
174
+ #
175
+ # Exception notifiers use <tt>rack.exception</tt> instead of
176
+ # <tt>rack.errors</tt>, so we need to support it.
177
+ #
178
+ # @since 0.5.0
179
+ # @api private
180
+ #
181
+ # @see Hanami::Action::Throwable::RACK_ERRORS
182
+ # @see http://www.rubydoc.info/github/rack/rack/file/SPEC#The_Error_Stream
183
+ # @see https://github.com/hanami/controller/issues/133
184
+ RACK_EXCEPTION = "rack.exception"
185
+
186
+ # The HTTP header for redirects
187
+ #
188
+ # @since 0.2.0
189
+ # @api private
190
+ LOCATION = "Location"
191
+
192
+ # The key that returns Rack session params from the Rack env
193
+ # Please note that this is used only when an action is unit tested.
194
+ #
195
+ # @since 2.0.0
196
+ # @api private
197
+ #
198
+ # @example
199
+ # # action unit test
200
+ # action.call("rack.session" => { "foo" => "bar" })
201
+ # action.session[:foo] # => "bar"
202
+ #
203
+ # @api private
204
+ RACK_SESSION = ::Rack::RACK_SESSION
205
+
206
+ # @since 2.0.0
207
+ # @api private
208
+ REQUEST_ID = "hanami.request_id"
209
+
210
+ # @since 2.0.0
211
+ # @api private
212
+ DEFAULT_ID_LENGTH = 16
213
+
214
+ # The key that returns raw cookies from the Rack env
215
+ #
216
+ # @since 2.0.0
217
+ # @api private
218
+ HTTP_COOKIE = ::Rack::HTTP_COOKIE
219
+
220
+ # The key used by Rack to set the cookies as an Hash in the env
221
+ #
222
+ # @since 2.0.0
223
+ # @api private
224
+ COOKIE_HASH_KEY = ::Rack::RACK_REQUEST_COOKIE_HASH
225
+
226
+ # The key used by Rack to set the cookies as a String in the env
227
+ #
228
+ # @since 2.0.0
229
+ # @api private
230
+ COOKIE_STRING_KEY = ::Rack::RACK_REQUEST_COOKIE_STRING
231
+
232
+ # The key that returns raw input from the Rack env
233
+ #
234
+ # @since 2.0.0
235
+ # @api private
236
+ RACK_INPUT = ::Rack::RACK_INPUT
237
+
238
+ # The key that returns router params from the Rack env
239
+ # This is a builtin integration for Hanami::Router
240
+ #
241
+ # @since 2.0.0
242
+ # @api private
243
+ ROUTER_PARAMS = "router.params"
244
+
245
+ # Default HTTP request method for Rack env
246
+ #
247
+ # @since 2.0.0
248
+ # @api private
249
+ DEFAULT_REQUEST_METHOD = GET
250
+
251
+ # @since 2.0.0
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"
260
+ end
261
+ end