hanami-action 3.0.0.rc1
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +985 -0
- data/LICENSE +20 -0
- data/README.md +873 -0
- data/hanami-action.gemspec +39 -0
- data/lib/hanami/action/body_parser/json.rb +20 -0
- data/lib/hanami/action/body_parser/multipart_form.rb +22 -0
- data/lib/hanami/action/body_parser.rb +109 -0
- data/lib/hanami/action/cache/cache_control.rb +84 -0
- data/lib/hanami/action/cache/conditional_get.rb +101 -0
- data/lib/hanami/action/cache/directives.rb +126 -0
- data/lib/hanami/action/cache/expires.rb +84 -0
- data/lib/hanami/action/cache.rb +29 -0
- data/lib/hanami/action/config/formats.rb +256 -0
- data/lib/hanami/action/config.rb +172 -0
- data/lib/hanami/action/constants.rb +283 -0
- data/lib/hanami/action/cookie_jar.rb +214 -0
- data/lib/hanami/action/cookies.rb +27 -0
- data/lib/hanami/action/csrf_protection.rb +217 -0
- data/lib/hanami/action/errors.rb +109 -0
- data/lib/hanami/action/flash.rb +176 -0
- data/lib/hanami/action/halt.rb +18 -0
- data/lib/hanami/action/mime/request_mime_weight.rb +66 -0
- data/lib/hanami/action/mime.rb +438 -0
- data/lib/hanami/action/params.rb +342 -0
- data/lib/hanami/action/rack/file.rb +41 -0
- data/lib/hanami/action/rack_utils.rb +11 -0
- data/lib/hanami/action/request/session.rb +68 -0
- data/lib/hanami/action/request.rb +141 -0
- data/lib/hanami/action/response.rb +481 -0
- data/lib/hanami/action/session.rb +47 -0
- data/lib/hanami/action/validatable.rb +166 -0
- data/lib/hanami/action/version.rb +13 -0
- data/lib/hanami/action/view_name_inferrer.rb +56 -0
- data/lib/hanami/action.rb +672 -0
- data/lib/hanami/http/status.rb +149 -0
- data/lib/hanami-action.rb +3 -0
- metadata +153 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rack/request"
|
|
4
|
+
require "hanami/utils/hash"
|
|
5
|
+
|
|
6
|
+
module Hanami
|
|
7
|
+
class Action
|
|
8
|
+
# Provides access to params included in a Rack request.
|
|
9
|
+
#
|
|
10
|
+
# Offers useful access to params via methods like {#[]}, {#get} and {#to_h}.
|
|
11
|
+
#
|
|
12
|
+
# These params are available via {Request#params}.
|
|
13
|
+
#
|
|
14
|
+
# This class is used by default when {Hanami::Action::Validatable} is not included, or when no
|
|
15
|
+
# {Validatable::ClassMethods#params params} validation schema is defined.
|
|
16
|
+
#
|
|
17
|
+
# @see Hanami::Action::Request#params
|
|
18
|
+
|
|
19
|
+
# A set of params requested by the client.
|
|
20
|
+
#
|
|
21
|
+
# Extracts the relevant params from a Rack env (query string, request body, and route params
|
|
22
|
+
# placed in `env["router.params"]` by the router), or from a plain hash passed for convenience
|
|
23
|
+
# in tests.
|
|
24
|
+
#
|
|
25
|
+
# @since 0.1.0
|
|
26
|
+
class Params
|
|
27
|
+
# @api private
|
|
28
|
+
EMPTY_PARAMS = {}.freeze
|
|
29
|
+
|
|
30
|
+
# Params errors
|
|
31
|
+
#
|
|
32
|
+
# @since 1.1.0
|
|
33
|
+
class Errors < SimpleDelegator
|
|
34
|
+
# @since 1.1.0
|
|
35
|
+
# @api private
|
|
36
|
+
def initialize(errors = {})
|
|
37
|
+
super(errors.dup)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Add an error to the param validations
|
|
41
|
+
#
|
|
42
|
+
# This has a semantic similar to `Hash#dig` where you use a set of keys
|
|
43
|
+
# to get a nested value, here you use a set of keys to set a nested
|
|
44
|
+
# value.
|
|
45
|
+
#
|
|
46
|
+
# @param args [Array<Symbol, String>] an array of arguments: the last
|
|
47
|
+
# one is the message to add (String), while the beginning of the array
|
|
48
|
+
# is made of keys to reach the attribute.
|
|
49
|
+
#
|
|
50
|
+
# @raise [ArgumentError] when try to add a message for a key that is
|
|
51
|
+
# already filled with incompatible message type.
|
|
52
|
+
# This usually happens with nested attributes: if you have a `:book`
|
|
53
|
+
# schema and the input doesn't include data for `:book`, the messages
|
|
54
|
+
# will be `["is missing"]`. In that case you can't add an error for a
|
|
55
|
+
# key nested under `:book`.
|
|
56
|
+
#
|
|
57
|
+
# @since 1.1.0
|
|
58
|
+
#
|
|
59
|
+
# @example Basic usage
|
|
60
|
+
# require "hanami/action"
|
|
61
|
+
#
|
|
62
|
+
# class MyAction < Hanami::Action
|
|
63
|
+
# params do
|
|
64
|
+
# required(:book).schema do
|
|
65
|
+
# required(:isbn).filled(:str?)
|
|
66
|
+
# end
|
|
67
|
+
# end
|
|
68
|
+
#
|
|
69
|
+
# def handle(request, response)
|
|
70
|
+
# # 1. Don't try to save the record if the params aren't valid
|
|
71
|
+
# return unless request.params.valid?
|
|
72
|
+
#
|
|
73
|
+
# BookRepository.new.create(request.params[:book])
|
|
74
|
+
# rescue Hanami::Model::UniqueConstraintViolationError
|
|
75
|
+
# # 2. Add an error in case the record wasn't unique
|
|
76
|
+
# request.params.errors.add(:book, :isbn, "is not unique")
|
|
77
|
+
# end
|
|
78
|
+
# end
|
|
79
|
+
#
|
|
80
|
+
# @example Invalid argument
|
|
81
|
+
# require "hanami/action"
|
|
82
|
+
#
|
|
83
|
+
# class MyAction < Hanami::Action
|
|
84
|
+
# params do
|
|
85
|
+
# required(:book).schema do
|
|
86
|
+
# required(:title).filled(:str?)
|
|
87
|
+
# end
|
|
88
|
+
# end
|
|
89
|
+
#
|
|
90
|
+
# def handle(request, *)
|
|
91
|
+
# puts request.params.to_h # => {}
|
|
92
|
+
# puts request.params.valid? # => false
|
|
93
|
+
# puts request.params.error_messages # => ["Book is missing"]
|
|
94
|
+
# puts request.params.errors # => {:book=>["is missing"]}
|
|
95
|
+
#
|
|
96
|
+
# request.params.errors.add(:book, :isbn, "is not unique") # => ArgumentError
|
|
97
|
+
# end
|
|
98
|
+
# end
|
|
99
|
+
def add(*args)
|
|
100
|
+
*keys, key, error = args
|
|
101
|
+
_nested_attribute(keys, key) << error
|
|
102
|
+
rescue TypeError
|
|
103
|
+
raise ArgumentError.new("Can't add #{args.map(&:inspect).join(', ')} to #{inspect}")
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
# @since 1.1.0
|
|
109
|
+
# @api private
|
|
110
|
+
def _nested_attribute(keys, key)
|
|
111
|
+
if keys.empty?
|
|
112
|
+
self
|
|
113
|
+
else
|
|
114
|
+
keys.inject(self) { |result, k| result[k] ||= {} }
|
|
115
|
+
dig(*keys)
|
|
116
|
+
end[key] ||= []
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# @attr_reader env [Hash] the Rack env
|
|
121
|
+
#
|
|
122
|
+
# @since 0.7.0
|
|
123
|
+
# @api private
|
|
124
|
+
attr_reader :env
|
|
125
|
+
|
|
126
|
+
# @attr_reader raw [Hash] the raw params from the request
|
|
127
|
+
#
|
|
128
|
+
# @since 0.7.0
|
|
129
|
+
# @api private
|
|
130
|
+
attr_reader :raw
|
|
131
|
+
|
|
132
|
+
# Returns structured error messages
|
|
133
|
+
#
|
|
134
|
+
# @return [Hash]
|
|
135
|
+
#
|
|
136
|
+
# @since 0.7.0
|
|
137
|
+
#
|
|
138
|
+
# @example
|
|
139
|
+
# params.errors
|
|
140
|
+
# # => {
|
|
141
|
+
# :email=>["is missing", "is in invalid format"],
|
|
142
|
+
# :name=>["is missing"],
|
|
143
|
+
# :tos=>["is missing"],
|
|
144
|
+
# :age=>["is missing"],
|
|
145
|
+
# :address=>["is missing"]
|
|
146
|
+
# }
|
|
147
|
+
attr_reader :errors
|
|
148
|
+
|
|
149
|
+
# Initialize the params and freeze them.
|
|
150
|
+
#
|
|
151
|
+
# @param env [Hash] a Rack env or an hash of params.
|
|
152
|
+
#
|
|
153
|
+
# @return [Params]
|
|
154
|
+
#
|
|
155
|
+
# @since 0.1.0
|
|
156
|
+
# @api private
|
|
157
|
+
def initialize(env:, contract: nil)
|
|
158
|
+
@env = env
|
|
159
|
+
@raw = _extract_params
|
|
160
|
+
|
|
161
|
+
if contract
|
|
162
|
+
validation = contract.call(raw)
|
|
163
|
+
@params = validation.to_h
|
|
164
|
+
@errors = Errors.new(validation.errors.to_h)
|
|
165
|
+
else
|
|
166
|
+
@params = raw.empty? ? EMPTY_PARAMS : Utils::Hash.deep_symbolize(raw)
|
|
167
|
+
@errors = Errors.new
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
freeze
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Returns the value for the given params key.
|
|
174
|
+
#
|
|
175
|
+
# @param key [Symbol] the key
|
|
176
|
+
#
|
|
177
|
+
# @return [Object,nil] the associated value, if found
|
|
178
|
+
#
|
|
179
|
+
# @since 0.7.0
|
|
180
|
+
# @api public
|
|
181
|
+
def [](key)
|
|
182
|
+
@params[key]
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Returns an value associated with the given params key.
|
|
186
|
+
#
|
|
187
|
+
# You can access nested attributes by listing all the keys in the path. This uses the same key
|
|
188
|
+
# path semantics as `Hash#dig`.
|
|
189
|
+
#
|
|
190
|
+
# @param keys [Array<Symbol,Integer>] the key
|
|
191
|
+
#
|
|
192
|
+
# @return [Object,NilClass] return the associated value, if found
|
|
193
|
+
#
|
|
194
|
+
# @example
|
|
195
|
+
# require "hanami/action"
|
|
196
|
+
#
|
|
197
|
+
# module Deliveries
|
|
198
|
+
# class Create < Hanami::Action
|
|
199
|
+
# def handle(request, *)
|
|
200
|
+
# request.params.get(:customer_name) # => "Luca"
|
|
201
|
+
# request.params.get(:uknown) # => nil
|
|
202
|
+
#
|
|
203
|
+
# request.params.get(:address, :city) # => "Rome"
|
|
204
|
+
# request.params.get(:address, :unknown) # => nil
|
|
205
|
+
#
|
|
206
|
+
# request.params.get(:tags, 0) # => "foo"
|
|
207
|
+
# request.params.get(:tags, 1) # => "bar"
|
|
208
|
+
# request.params.get(:tags, 999) # => nil
|
|
209
|
+
#
|
|
210
|
+
# request.params.get(nil) # => nil
|
|
211
|
+
# end
|
|
212
|
+
# end
|
|
213
|
+
# end
|
|
214
|
+
#
|
|
215
|
+
# @since 0.7.0
|
|
216
|
+
# @api public
|
|
217
|
+
def get(*keys)
|
|
218
|
+
@params.dig(*keys)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# This is for compatibility with Hanami::Helpers::FormHelper::Values
|
|
222
|
+
#
|
|
223
|
+
# @api private
|
|
224
|
+
# @since 0.8.0
|
|
225
|
+
alias_method :dig, :get
|
|
226
|
+
|
|
227
|
+
# Returns flat collection of full error messages
|
|
228
|
+
#
|
|
229
|
+
# @return [Array]
|
|
230
|
+
#
|
|
231
|
+
# @since 0.7.0
|
|
232
|
+
#
|
|
233
|
+
# @example
|
|
234
|
+
# params.error_messages
|
|
235
|
+
# # => [
|
|
236
|
+
# "Email is missing",
|
|
237
|
+
# "Email is in invalid format",
|
|
238
|
+
# "Name is missing",
|
|
239
|
+
# "Tos is missing",
|
|
240
|
+
# "Age is missing",
|
|
241
|
+
# "Address is missing"
|
|
242
|
+
# ]
|
|
243
|
+
def error_messages(error_set = errors)
|
|
244
|
+
error_set.each_with_object([]) do |(key, messages), result|
|
|
245
|
+
k = Utils::String.titleize(key)
|
|
246
|
+
|
|
247
|
+
msgs = if messages.is_a?(::Hash)
|
|
248
|
+
error_messages(messages)
|
|
249
|
+
else
|
|
250
|
+
messages.map { |message| "#{k} #{message}" }
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
result.concat(msgs)
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Returns true if no validation errors are found,
|
|
258
|
+
# false otherwise.
|
|
259
|
+
#
|
|
260
|
+
# @return [TrueClass, FalseClass]
|
|
261
|
+
#
|
|
262
|
+
# @since 0.7.0
|
|
263
|
+
#
|
|
264
|
+
# @example
|
|
265
|
+
# params.valid? # => true
|
|
266
|
+
def valid?
|
|
267
|
+
errors.empty?
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Iterates over the params.
|
|
271
|
+
#
|
|
272
|
+
# Calls the given block with each param key-value pair; returns the full hash of params.
|
|
273
|
+
#
|
|
274
|
+
# @yieldparam key [Symbol]
|
|
275
|
+
# @yieldparam value [Object]
|
|
276
|
+
#
|
|
277
|
+
# @return [to_h]
|
|
278
|
+
#
|
|
279
|
+
# @since 0.7.1
|
|
280
|
+
# @api public
|
|
281
|
+
def each(&blk)
|
|
282
|
+
to_h.each(&blk)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# Serialize validated params to Hash
|
|
286
|
+
#
|
|
287
|
+
# @return [::Hash]
|
|
288
|
+
#
|
|
289
|
+
# @since 0.3.0
|
|
290
|
+
def to_h
|
|
291
|
+
@params
|
|
292
|
+
end
|
|
293
|
+
alias_method :to_hash, :to_h
|
|
294
|
+
|
|
295
|
+
# Pattern-matching support
|
|
296
|
+
#
|
|
297
|
+
# @return [::Hash]
|
|
298
|
+
#
|
|
299
|
+
# @since 2.0.2
|
|
300
|
+
def deconstruct_keys(*)
|
|
301
|
+
to_hash
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
private
|
|
305
|
+
|
|
306
|
+
def _extract_params # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
307
|
+
# Without PATH_INFO (URL path from a server) or rack.input (request body), env has no
|
|
308
|
+
# Rack-sourced data for us to parse as params; it's likely a bare hash passed to
|
|
309
|
+
# `Action#call` for convenience in tests. In this case, treat the env itself as the params.
|
|
310
|
+
return env unless env.key?(PATH_INFO) || env.key?(RACK_INPUT)
|
|
311
|
+
|
|
312
|
+
query_string = env[::Rack::QUERY_STRING]
|
|
313
|
+
has_query = query_string && !query_string.empty?
|
|
314
|
+
has_router_params = env.key?(ROUTER_PARAMS)
|
|
315
|
+
has_body_params = env.key?(ACTION_BODY_PARAMS)
|
|
316
|
+
# Only a form-urlencoded/multipart body yields params via Rack; other bodies (e.g. JSON)
|
|
317
|
+
# are handled by BodyParser, which sets ACTION_BODY_PARAMS.
|
|
318
|
+
has_form_body =
|
|
319
|
+
env.key?(RACK_INPUT) && !has_body_params && _form_content_type?(env[CONTENT_TYPE])
|
|
320
|
+
|
|
321
|
+
# Fast path: nothing in env produces params, so avoid allocating a Rack::Request.
|
|
322
|
+
return EMPTY_PARAMS unless has_query || has_form_body || has_router_params || has_body_params
|
|
323
|
+
|
|
324
|
+
result = {}
|
|
325
|
+
rack_request = ::Rack::Request.new(env) if has_query || has_form_body
|
|
326
|
+
|
|
327
|
+
result.merge!(rack_request.GET) if has_query
|
|
328
|
+
result.merge!(rack_request.POST) if has_form_body
|
|
329
|
+
result.merge!(env[ROUTER_PARAMS]) if has_router_params
|
|
330
|
+
result.merge!(env[ACTION_BODY_PARAMS]) if has_body_params
|
|
331
|
+
|
|
332
|
+
result
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def _form_content_type?(content_type)
|
|
336
|
+
return false unless content_type
|
|
337
|
+
|
|
338
|
+
content_type.start_with?("application/x-www-form-urlencoded", "multipart/form-data")
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rack/files"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
class Action
|
|
7
|
+
# Rack extensions for actions.
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
10
|
+
# @since 0.4.3
|
|
11
|
+
module Rack
|
|
12
|
+
# File to be sent
|
|
13
|
+
#
|
|
14
|
+
# @see Hanami::Action::Response#send_file
|
|
15
|
+
#
|
|
16
|
+
# @since 0.4.3
|
|
17
|
+
# @api private
|
|
18
|
+
class File
|
|
19
|
+
# @param path [String,Pathname] file path
|
|
20
|
+
#
|
|
21
|
+
# @since 0.4.3
|
|
22
|
+
# @api private
|
|
23
|
+
def initialize(path, root)
|
|
24
|
+
@file = ::Rack::Files.new(root.to_s)
|
|
25
|
+
@path = path.to_s
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @since 0.4.3
|
|
29
|
+
# @api private
|
|
30
|
+
def call(env)
|
|
31
|
+
env = env.dup
|
|
32
|
+
env[Action::PATH_INFO] = @path
|
|
33
|
+
|
|
34
|
+
@file.get(env)
|
|
35
|
+
rescue Errno::ENOENT
|
|
36
|
+
[Action::NOT_FOUND, {}, nil]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "forwardable"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
class Action
|
|
7
|
+
class Request < ::Rack::Request
|
|
8
|
+
# Wrapper for Rack-provided sessions, allowing access using symbol keys.
|
|
9
|
+
#
|
|
10
|
+
# @since 2.3.0
|
|
11
|
+
# @api public
|
|
12
|
+
class Session
|
|
13
|
+
extend Forwardable
|
|
14
|
+
|
|
15
|
+
def_delegators \
|
|
16
|
+
:@session,
|
|
17
|
+
:clear,
|
|
18
|
+
:delete,
|
|
19
|
+
:empty?,
|
|
20
|
+
:size,
|
|
21
|
+
:length,
|
|
22
|
+
:each,
|
|
23
|
+
:to_h,
|
|
24
|
+
:inspect,
|
|
25
|
+
:keys,
|
|
26
|
+
:values
|
|
27
|
+
|
|
28
|
+
def initialize(session)
|
|
29
|
+
@session = session
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def [](key)
|
|
33
|
+
@session[key.to_s]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def []=(key, value)
|
|
37
|
+
@session[key.to_s] = value
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def key?(key)
|
|
41
|
+
@session.key?(key.to_s)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
alias_method :has_key?, :key?
|
|
45
|
+
alias_method :include?, :key?
|
|
46
|
+
|
|
47
|
+
def ==(other)
|
|
48
|
+
Utils::Hash.deep_symbolize(@session) == Utils::Hash.deep_symbolize(other)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
# Provides a fallback for any methods not handled by the def_delegators.
|
|
54
|
+
def method_missing(method_name, *args, &block)
|
|
55
|
+
if @session.respond_to?(method_name)
|
|
56
|
+
@session.send(method_name, *args, &block)
|
|
57
|
+
else
|
|
58
|
+
super
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
63
|
+
@session.respond_to?(method_name, include_private) || super
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rack/mime"
|
|
4
|
+
require "rack/request"
|
|
5
|
+
require "rack/utils"
|
|
6
|
+
require "securerandom"
|
|
7
|
+
require_relative "errors"
|
|
8
|
+
|
|
9
|
+
module Hanami
|
|
10
|
+
class Action
|
|
11
|
+
# The HTTP request for an action, given to {Action#handle}.
|
|
12
|
+
#
|
|
13
|
+
# Inherits from `Rack::Request`, providing compatibility with Rack functionality.
|
|
14
|
+
#
|
|
15
|
+
# @see http://www.rubydoc.info/gems/rack/Rack/Request
|
|
16
|
+
#
|
|
17
|
+
# @since 0.3.1
|
|
18
|
+
class Request < ::Rack::Request
|
|
19
|
+
# Returns the request's params.
|
|
20
|
+
#
|
|
21
|
+
# @return [Params]
|
|
22
|
+
#
|
|
23
|
+
# @since 2.0.0
|
|
24
|
+
# @api public
|
|
25
|
+
attr_reader :params
|
|
26
|
+
|
|
27
|
+
# @since 2.0.0
|
|
28
|
+
# @api private
|
|
29
|
+
def initialize(env:, params:, default_tld_length: 1, session_enabled: false)
|
|
30
|
+
super(env)
|
|
31
|
+
|
|
32
|
+
@params = params
|
|
33
|
+
@session_enabled = session_enabled
|
|
34
|
+
@default_tld_length = default_tld_length
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Returns the request's ID
|
|
38
|
+
#
|
|
39
|
+
# @return [String]
|
|
40
|
+
#
|
|
41
|
+
# @since 2.0.0
|
|
42
|
+
# @api public
|
|
43
|
+
def id
|
|
44
|
+
# FIXME: make this number configurable and document the probabilities of clashes
|
|
45
|
+
@id ||= @env[Action::REQUEST_ID] = SecureRandom.hex(Action::DEFAULT_ID_LENGTH)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Returns true if the session is enabled for the request.
|
|
49
|
+
#
|
|
50
|
+
# @return [Boolean]
|
|
51
|
+
#
|
|
52
|
+
# @api public
|
|
53
|
+
# @since 2.1.0
|
|
54
|
+
def session_enabled?
|
|
55
|
+
@session_enabled
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Returns the session for the request.
|
|
59
|
+
#
|
|
60
|
+
# @return [Hanami::Request::Session] the session object
|
|
61
|
+
#
|
|
62
|
+
# @raise [MissingSessionError] if the session is not enabled
|
|
63
|
+
#
|
|
64
|
+
# @see #session_enabled?
|
|
65
|
+
# @see Response#session
|
|
66
|
+
#
|
|
67
|
+
# @since 2.0.0
|
|
68
|
+
# @api public
|
|
69
|
+
def session
|
|
70
|
+
unless session_enabled?
|
|
71
|
+
raise Hanami::Action::MissingSessionError.new("Hanami::Action::Request#session")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
@session ||= Session.new(super)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Returns the flash for the request.
|
|
78
|
+
#
|
|
79
|
+
# @return [Flash]
|
|
80
|
+
#
|
|
81
|
+
# @raise [MissingSessionError] if sessions are not enabled
|
|
82
|
+
#
|
|
83
|
+
# @see Response#flash
|
|
84
|
+
#
|
|
85
|
+
# @since 2.0.0
|
|
86
|
+
# @api public
|
|
87
|
+
def flash
|
|
88
|
+
unless session_enabled?
|
|
89
|
+
raise Hanami::Action::MissingSessionError.new("Hanami::Action::Request#flash")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
@flash ||= Flash.new(session[Flash::KEY])
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Returns the subdomains for the current host.
|
|
96
|
+
#
|
|
97
|
+
# @return [Array<String>]
|
|
98
|
+
#
|
|
99
|
+
# @api public
|
|
100
|
+
# @since 2.3.0
|
|
101
|
+
def subdomains(tld_length = @default_tld_length)
|
|
102
|
+
return [] if IP_ADDRESS_HOST_REGEXP.match?(host)
|
|
103
|
+
|
|
104
|
+
host.split(".")[0..-(tld_length + 2)]
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
IP_ADDRESS_HOST_REGEXP = /\A\d+\.\d+\.\d+\.\d+\z/
|
|
108
|
+
private_constant :IP_ADDRESS_HOST_REGEXP
|
|
109
|
+
|
|
110
|
+
# Returns the subdomain for the current host.
|
|
111
|
+
#
|
|
112
|
+
# @return [String]
|
|
113
|
+
#
|
|
114
|
+
# @api public
|
|
115
|
+
# @since 2.3.0
|
|
116
|
+
def subdomain(tld_length = @default_tld_length)
|
|
117
|
+
subdomains(tld_length).join(".")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# @since 2.0.0
|
|
121
|
+
# @api private
|
|
122
|
+
def accept?(mime_type)
|
|
123
|
+
!!::Rack::Utils.q_values(accept).find do |mime, _|
|
|
124
|
+
::Rack::Mime.match?(mime_type, mime)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# @since 2.0.0
|
|
129
|
+
# @api private
|
|
130
|
+
def accept_header?
|
|
131
|
+
accept != Action::DEFAULT_ACCEPT
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# @since 0.1.0
|
|
135
|
+
# @api private
|
|
136
|
+
def accept
|
|
137
|
+
@accept ||= @env[Action::HTTP_ACCEPT] || Action::DEFAULT_ACCEPT
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|