hanami-controller 1.3.0 → 2.0.0.alpha2
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 +4 -4
- data/CHANGELOG.md +83 -0
- data/LICENSE.md +1 -1
- data/README.md +297 -538
- data/hanami-controller.gemspec +6 -5
- data/lib/hanami/action.rb +129 -73
- data/lib/hanami/action/application_action.rb +111 -0
- data/lib/hanami/action/application_configuration.rb +92 -0
- data/lib/hanami/action/application_configuration/cookies.rb +29 -0
- data/lib/hanami/action/application_configuration/sessions.rb +46 -0
- data/lib/hanami/action/base_params.rb +2 -2
- data/lib/hanami/action/cache.rb +1 -139
- data/lib/hanami/action/cache/cache_control.rb +4 -4
- data/lib/hanami/action/cache/conditional_get.rb +7 -2
- data/lib/hanami/action/cache/directives.rb +1 -1
- data/lib/hanami/action/cache/expires.rb +3 -3
- data/lib/hanami/action/configuration.rb +430 -0
- data/lib/hanami/action/cookie_jar.rb +3 -3
- data/lib/hanami/action/cookies.rb +3 -62
- data/lib/hanami/action/csrf_protection.rb +214 -0
- data/lib/hanami/action/flash.rb +102 -207
- data/lib/hanami/action/glue.rb +5 -31
- data/lib/hanami/action/halt.rb +12 -0
- data/lib/hanami/action/mime.rb +78 -485
- data/lib/hanami/action/params.rb +3 -3
- data/lib/hanami/action/rack/file.rb +1 -1
- data/lib/hanami/action/request.rb +30 -20
- data/lib/hanami/action/response.rb +193 -0
- data/lib/hanami/action/session.rb +11 -128
- data/lib/hanami/action/standalone_action.rb +581 -0
- data/lib/hanami/action/validatable.rb +2 -2
- data/lib/hanami/action/view_name_inferrer.rb +46 -0
- data/lib/hanami/controller.rb +0 -227
- data/lib/hanami/controller/version.rb +1 -1
- data/lib/hanami/http/status.rb +2 -2
- metadata +47 -30
- data/lib/hanami-controller.rb +0 -1
- data/lib/hanami/action/callable.rb +0 -92
- data/lib/hanami/action/callbacks.rb +0 -214
- data/lib/hanami/action/configurable.rb +0 -50
- data/lib/hanami/action/exposable.rb +0 -126
- data/lib/hanami/action/exposable/guard.rb +0 -104
- data/lib/hanami/action/head.rb +0 -121
- data/lib/hanami/action/rack.rb +0 -399
- data/lib/hanami/action/rack/callable.rb +0 -47
- data/lib/hanami/action/redirect.rb +0 -59
- data/lib/hanami/action/throwable.rb +0 -196
- data/lib/hanami/controller/configuration.rb +0 -763
@@ -1,104 +0,0 @@
|
|
1
|
-
require 'hanami/controller/error'
|
2
|
-
|
3
|
-
module Hanami
|
4
|
-
module Controller
|
5
|
-
# Exposure of reserved words
|
6
|
-
#
|
7
|
-
# @since 0.7.1
|
8
|
-
class IllegalExposureError < Error
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
module Action
|
13
|
-
module Exposable
|
14
|
-
# Guard for Exposures API.
|
15
|
-
# Prevents exposure of reserved words
|
16
|
-
#
|
17
|
-
# @since 0.7.1
|
18
|
-
# @api private
|
19
|
-
#
|
20
|
-
# @see Hanami::Action::Exposable::Guard::ClassMethods#expose
|
21
|
-
# @see Hanami::Action::Exposable::Guard::ClassMethods#reserved_word?
|
22
|
-
module Guard
|
23
|
-
# Override Ruby's hook for modules.
|
24
|
-
# It prepends a guard for the exposures logic
|
25
|
-
#
|
26
|
-
# @param base [Class] the target action
|
27
|
-
#
|
28
|
-
# @since 0.7.1
|
29
|
-
# @api private
|
30
|
-
#
|
31
|
-
# @see http://www.ruby-doc.org/core-2.1.2/Module.html#method-i-included
|
32
|
-
def self.included(base)
|
33
|
-
class << base
|
34
|
-
prepend ClassMethods
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# Exposures API Guard class methods
|
39
|
-
#
|
40
|
-
# @since 0.7.1
|
41
|
-
# @api private
|
42
|
-
module ClassMethods
|
43
|
-
# Prevents exposure if names contain a reserved word.
|
44
|
-
#
|
45
|
-
# @param names [Array<Symbol>] the name(s) of the attribute(s) to be
|
46
|
-
# exposed
|
47
|
-
#
|
48
|
-
# @return [void]
|
49
|
-
#
|
50
|
-
# @since 0.7.1
|
51
|
-
# @api private
|
52
|
-
def expose(*names)
|
53
|
-
detect_reserved_words!(names)
|
54
|
-
|
55
|
-
super
|
56
|
-
end
|
57
|
-
|
58
|
-
private
|
59
|
-
|
60
|
-
# Raises error if given names contain a reserved word.
|
61
|
-
#
|
62
|
-
# @param names [Array<Symbol>] a list of names to be checked.
|
63
|
-
#
|
64
|
-
# @return [void]
|
65
|
-
#
|
66
|
-
# @raise [IllegalExposeError] if names contain one or more of reserved
|
67
|
-
# words
|
68
|
-
#
|
69
|
-
# @since 0.7.1
|
70
|
-
# @api private
|
71
|
-
def detect_reserved_words!(names)
|
72
|
-
names.each do |name|
|
73
|
-
if reserved_word?(name)
|
74
|
-
raise Hanami::Controller::IllegalExposureError.new("#{name} is a reserved word. It cannot be exposed")
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
# Checks if a string is a reserved word
|
80
|
-
#
|
81
|
-
# Reserved word is a name of the method defined in one of the modules
|
82
|
-
# of a given namespace.
|
83
|
-
#
|
84
|
-
# @param name [Symbol] the word to be checked
|
85
|
-
# @param namespace [String] the namespace containing internal modules
|
86
|
-
#
|
87
|
-
# @return [true, false]
|
88
|
-
#
|
89
|
-
# @since 0.7.1
|
90
|
-
# @api private
|
91
|
-
def reserved_word?(name, namespace = 'Hanami')
|
92
|
-
if method_defined?(name) || private_method_defined?(name)
|
93
|
-
method_owner = instance_method(name).owner
|
94
|
-
|
95
|
-
Utils::String.namespace(method_owner) == namespace
|
96
|
-
else
|
97
|
-
false
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
data/lib/hanami/action/head.rb
DELETED
@@ -1,121 +0,0 @@
|
|
1
|
-
module Hanami
|
2
|
-
module Action
|
3
|
-
# Ensures to not send body or headers for HEAD requests and/or for status
|
4
|
-
# codes that doesn't allow them.
|
5
|
-
#
|
6
|
-
# @since 0.3.2
|
7
|
-
#
|
8
|
-
# @see http://www.ietf.org/rfc/rfc2616.txt
|
9
|
-
module Head
|
10
|
-
|
11
|
-
# Status codes that by RFC must not include a message body
|
12
|
-
#
|
13
|
-
# @since 0.3.2
|
14
|
-
# @api private
|
15
|
-
HTTP_STATUSES_WITHOUT_BODY = Set.new((100..199).to_a << 204 << 205 << 304).freeze
|
16
|
-
|
17
|
-
|
18
|
-
# Entity headers allowed in blank body responses, according to
|
19
|
-
# RFC 2616 - Section 10 (HTTP 1.1).
|
20
|
-
#
|
21
|
-
# "The response MAY include new or updated metainformation in the form
|
22
|
-
# of entity-headers".
|
23
|
-
#
|
24
|
-
# @since 0.4.0
|
25
|
-
# @api private
|
26
|
-
#
|
27
|
-
# @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.5
|
28
|
-
# @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html
|
29
|
-
ENTITY_HEADERS = {
|
30
|
-
'Allow' => true,
|
31
|
-
'Content-Encoding' => true,
|
32
|
-
'Content-Language' => true,
|
33
|
-
'Content-Location' => true,
|
34
|
-
'Content-MD5' => true,
|
35
|
-
'Content-Range' => true,
|
36
|
-
'Expires' => true,
|
37
|
-
'Last-Modified' => true,
|
38
|
-
'extension-header' => true
|
39
|
-
}.freeze
|
40
|
-
|
41
|
-
# Ensures to not send body or headers for HEAD requests and/or for status
|
42
|
-
# codes that doesn't allow them.
|
43
|
-
#
|
44
|
-
# @since 0.3.2
|
45
|
-
# @api private
|
46
|
-
#
|
47
|
-
# @see Hanami::Action#finish
|
48
|
-
def finish
|
49
|
-
super
|
50
|
-
|
51
|
-
if _requires_no_body?
|
52
|
-
@_body = nil
|
53
|
-
@headers.reject! {|header,_| !keep_response_header?(header) }
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
protected
|
58
|
-
# @since 0.3.2
|
59
|
-
# @api private
|
60
|
-
def _requires_no_body?
|
61
|
-
HTTP_STATUSES_WITHOUT_BODY.include?(@_status) || head?
|
62
|
-
end
|
63
|
-
|
64
|
-
private
|
65
|
-
# According to RFC 2616, when a response MUST have an empty body, it only
|
66
|
-
# allows Entity Headers.
|
67
|
-
#
|
68
|
-
# For instance, a <tt>204</tt> doesn't allow <tt>Content-Type</tt> or any
|
69
|
-
# other custom header.
|
70
|
-
#
|
71
|
-
# This restriction is enforced by <tt>Hanami::Action::Head#finish</tt>.
|
72
|
-
#
|
73
|
-
# However, there are cases that demand to bypass this rule to set meta
|
74
|
-
# informations via headers.
|
75
|
-
#
|
76
|
-
# An example is a <tt>DELETE</tt> request for a JSON API application.
|
77
|
-
# It returns a <tt>204</tt> but still wants to specify the rate limit
|
78
|
-
# quota via <tt>X-Rate-Limit</tt>.
|
79
|
-
#
|
80
|
-
# @since 0.5.0
|
81
|
-
#
|
82
|
-
# @see Hanami::Action::HEAD#finish
|
83
|
-
#
|
84
|
-
# @example
|
85
|
-
# require 'hanami/controller'
|
86
|
-
#
|
87
|
-
# module Books
|
88
|
-
# class Destroy
|
89
|
-
# include Hanami::Action
|
90
|
-
#
|
91
|
-
# def call(params)
|
92
|
-
# # ...
|
93
|
-
# self.headers.merge!(
|
94
|
-
# 'Last-Modified' => 'Fri, 27 Nov 2015 13:32:36 GMT',
|
95
|
-
# 'X-Rate-Limit' => '4000',
|
96
|
-
# 'Content-Type' => 'application/json',
|
97
|
-
# 'X-No-Pass' => 'true'
|
98
|
-
# )
|
99
|
-
#
|
100
|
-
# self.status = 204
|
101
|
-
# end
|
102
|
-
#
|
103
|
-
# private
|
104
|
-
#
|
105
|
-
# def keep_response_header?(header)
|
106
|
-
# super || header == 'X-Rate-Limit'
|
107
|
-
# end
|
108
|
-
# end
|
109
|
-
# end
|
110
|
-
#
|
111
|
-
# # Only the following headers will be sent:
|
112
|
-
# # * Last-Modified - because we used `super' in the method that respects the HTTP RFC
|
113
|
-
# # * X-Rate-Limit - because we explicitely allow it
|
114
|
-
#
|
115
|
-
# # Both Content-Type and X-No-Pass are removed because they're not allowed
|
116
|
-
def keep_response_header?(header)
|
117
|
-
ENTITY_HEADERS.include?(header)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
data/lib/hanami/action/rack.rb
DELETED
@@ -1,399 +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
|
-
# Override Ruby's hook for modules.
|
87
|
-
# It includes basic Hanami::Action modules to the given class.
|
88
|
-
#
|
89
|
-
# @param base [Class] the target action
|
90
|
-
#
|
91
|
-
# @since 0.1.0
|
92
|
-
# @api private
|
93
|
-
#
|
94
|
-
# @see http://www.ruby-doc.org/core-2.1.2/Module.html#method-i-included
|
95
|
-
def self.included(base)
|
96
|
-
base.class_eval do
|
97
|
-
extend ClassMethods
|
98
|
-
prepend InstanceMethods
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
# @api private
|
103
|
-
module ClassMethods
|
104
|
-
# Build rack builder
|
105
|
-
#
|
106
|
-
# @return [Rack::Builder]
|
107
|
-
# @api private
|
108
|
-
def rack_builder
|
109
|
-
@rack_builder ||= begin
|
110
|
-
extend Hanami::Action::Rack::Callable
|
111
|
-
rack_builder = ::Rack::Builder.new
|
112
|
-
rack_builder.run ->(env) { self.new.call(env) }
|
113
|
-
rack_builder
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
# Use a Rack middleware
|
118
|
-
#
|
119
|
-
# The middleware will be used as it is.
|
120
|
-
#
|
121
|
-
# At the runtime, the middleware be invoked with the raw Rack env.
|
122
|
-
#
|
123
|
-
# Multiple middlewares can be employed, just by using multiple times
|
124
|
-
# this method.
|
125
|
-
#
|
126
|
-
# @param middleware [#call] A Rack middleware
|
127
|
-
# @param args [Array] Array arguments for middleware
|
128
|
-
#
|
129
|
-
# @since 0.2.0
|
130
|
-
#
|
131
|
-
# @see Hanami::Action::Callbacks::ClassMethods#before
|
132
|
-
#
|
133
|
-
# @example Middleware
|
134
|
-
# require 'hanami/controller'
|
135
|
-
#
|
136
|
-
# module Sessions
|
137
|
-
# class Create
|
138
|
-
# include Hanami::Action
|
139
|
-
# use OmniAuth
|
140
|
-
#
|
141
|
-
# def call(params)
|
142
|
-
# # ...
|
143
|
-
# end
|
144
|
-
# end
|
145
|
-
# end
|
146
|
-
def use(middleware, *args, &block)
|
147
|
-
rack_builder.use middleware, *args, &block
|
148
|
-
end
|
149
|
-
|
150
|
-
# Returns the class which defines the params
|
151
|
-
#
|
152
|
-
# Returns the class which has been provided to define the
|
153
|
-
# params. By default this will be Hanami::Action::Params.
|
154
|
-
#
|
155
|
-
# @return [Class] A params class (when whitelisted) or
|
156
|
-
# Hanami::Action::Params
|
157
|
-
#
|
158
|
-
# @api private
|
159
|
-
# @since 0.7.0
|
160
|
-
def params_class
|
161
|
-
@params_class ||= BaseParams
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
# @since 0.7.0
|
166
|
-
# @api private
|
167
|
-
module InstanceMethods
|
168
|
-
# @since 0.7.0
|
169
|
-
# @api private
|
170
|
-
def initialize(*)
|
171
|
-
super
|
172
|
-
@_status = nil
|
173
|
-
@_body = nil
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
protected
|
178
|
-
# Gets the headers from the response
|
179
|
-
#
|
180
|
-
# @return [Hash] the HTTP headers from the response
|
181
|
-
#
|
182
|
-
# @since 0.1.0
|
183
|
-
#
|
184
|
-
# @example
|
185
|
-
# require 'hanami/controller'
|
186
|
-
#
|
187
|
-
# class Show
|
188
|
-
# include Hanami::Action
|
189
|
-
#
|
190
|
-
# def call(params)
|
191
|
-
# # ...
|
192
|
-
# self.headers # => { ... }
|
193
|
-
# self.headers.merge!({'X-Custom' => 'OK'})
|
194
|
-
# end
|
195
|
-
# end
|
196
|
-
def headers
|
197
|
-
@headers
|
198
|
-
end
|
199
|
-
|
200
|
-
# Returns a serialized Rack response (Array), according to the current
|
201
|
-
# status code, headers, and body.
|
202
|
-
#
|
203
|
-
# @return [Array] the serialized response
|
204
|
-
#
|
205
|
-
# @since 0.1.0
|
206
|
-
# @api private
|
207
|
-
#
|
208
|
-
# @see Hanami::Action::Rack::DEFAULT_RESPONSE_CODE
|
209
|
-
# @see Hanami::Action::Rack::DEFAULT_RESPONSE_BODY
|
210
|
-
# @see Hanami::Action::Rack#status=
|
211
|
-
# @see Hanami::Action::Rack#headers
|
212
|
-
# @see Hanami::Action::Rack#body=
|
213
|
-
def response
|
214
|
-
[ @_status || DEFAULT_RESPONSE_CODE, headers, @_body || DEFAULT_RESPONSE_BODY.dup ]
|
215
|
-
end
|
216
|
-
|
217
|
-
# Calculates an unique ID for the current request
|
218
|
-
#
|
219
|
-
# @return [String] The unique ID
|
220
|
-
#
|
221
|
-
# @since 0.3.0
|
222
|
-
def request_id
|
223
|
-
# FIXME make this number configurable and document the probabilities of clashes
|
224
|
-
@request_id ||= SecureRandom.hex(DEFAULT_REQUEST_ID_LENGTH)
|
225
|
-
end
|
226
|
-
|
227
|
-
# Returns a Hanami specialized rack request
|
228
|
-
#
|
229
|
-
# @return [Hanami::Action::Request] The request
|
230
|
-
#
|
231
|
-
# @since 0.3.1
|
232
|
-
#
|
233
|
-
# @example
|
234
|
-
# require 'hanami/controller'
|
235
|
-
#
|
236
|
-
# class Create
|
237
|
-
# include Hanami::Action
|
238
|
-
#
|
239
|
-
# def call(params)
|
240
|
-
# ip = request.ip
|
241
|
-
# secure = request.ssl?
|
242
|
-
# end
|
243
|
-
# end
|
244
|
-
def request
|
245
|
-
@request ||= ::Hanami::Action::Request.new(@_env)
|
246
|
-
end
|
247
|
-
|
248
|
-
# Return parsed request body
|
249
|
-
#
|
250
|
-
# @deprecated
|
251
|
-
def parsed_request_body
|
252
|
-
Hanami::Utils::Deprecation.new('#parsed_request_body is deprecated and it will be removed in future versions')
|
253
|
-
@_env.fetch(ROUTER_PARSED_BODY, nil)
|
254
|
-
end
|
255
|
-
|
256
|
-
private
|
257
|
-
|
258
|
-
# Sets the HTTP status code for the response
|
259
|
-
#
|
260
|
-
# @param status [Fixnum] an HTTP status code
|
261
|
-
# @return [void]
|
262
|
-
#
|
263
|
-
# @since 0.1.0
|
264
|
-
#
|
265
|
-
# @example
|
266
|
-
# require 'hanami/controller'
|
267
|
-
#
|
268
|
-
# class Create
|
269
|
-
# include Hanami::Action
|
270
|
-
#
|
271
|
-
# def call(params)
|
272
|
-
# # ...
|
273
|
-
# self.status = 201
|
274
|
-
# end
|
275
|
-
# end
|
276
|
-
def status=(status)
|
277
|
-
@_status = status
|
278
|
-
end
|
279
|
-
|
280
|
-
# Sets the body of the response
|
281
|
-
#
|
282
|
-
# @param body [String] the body of the response
|
283
|
-
# @return [void]
|
284
|
-
#
|
285
|
-
# @since 0.1.0
|
286
|
-
#
|
287
|
-
# @example
|
288
|
-
# require 'hanami/controller'
|
289
|
-
#
|
290
|
-
# class Show
|
291
|
-
# include Hanami::Action
|
292
|
-
#
|
293
|
-
# def call(params)
|
294
|
-
# # ...
|
295
|
-
# self.body = 'Hi!'
|
296
|
-
# end
|
297
|
-
# end
|
298
|
-
def body=(body)
|
299
|
-
body = Array(body) unless body.respond_to?(:each)
|
300
|
-
@_body = body
|
301
|
-
end
|
302
|
-
|
303
|
-
# Send a file as response.
|
304
|
-
# <tt>This method only sends files from the public directory</tt>
|
305
|
-
#
|
306
|
-
# It automatically handle the following cases:
|
307
|
-
#
|
308
|
-
# * <tt>Content-Type</tt> and <tt>Content-Length</tt>
|
309
|
-
# * File Not found (returns a 404)
|
310
|
-
# * Conditional GET (via <tt>If-Modified-Since</tt> header)
|
311
|
-
# * Range requests (via <tt>Range</tt> header)
|
312
|
-
#
|
313
|
-
# @param path [String, Pathname] the body of the response
|
314
|
-
# @return [void]
|
315
|
-
#
|
316
|
-
# @since 0.4.3
|
317
|
-
#
|
318
|
-
# @example
|
319
|
-
# require 'hanami/controller'
|
320
|
-
#
|
321
|
-
# class Show
|
322
|
-
# include Hanami::Action
|
323
|
-
#
|
324
|
-
# def call(params)
|
325
|
-
# # ...
|
326
|
-
# send_file Pathname.new('path/to/file')
|
327
|
-
# end
|
328
|
-
# end
|
329
|
-
def send_file(path)
|
330
|
-
_send_file(
|
331
|
-
File.new(path, self.class.configuration.public_directory).call(@_env)
|
332
|
-
)
|
333
|
-
end
|
334
|
-
|
335
|
-
# Send a file as response from anywhere in the file system.
|
336
|
-
#
|
337
|
-
# @see Hanami::Action::Rack#send_file
|
338
|
-
#
|
339
|
-
# @param path [String, Pathname] path to the file to be sent
|
340
|
-
# @return [void]
|
341
|
-
#
|
342
|
-
# @since 1.0.0
|
343
|
-
#
|
344
|
-
# @example
|
345
|
-
# require 'hanami/controller'
|
346
|
-
#
|
347
|
-
# class Show
|
348
|
-
# include Hanami::Action
|
349
|
-
#
|
350
|
-
# def call(params)
|
351
|
-
# # ...
|
352
|
-
# unsafe_send_file Pathname.new('/tmp/path/to/file')
|
353
|
-
# end
|
354
|
-
# end
|
355
|
-
def unsafe_send_file(path)
|
356
|
-
directory = self.class.configuration.root_directory if Pathname.new(path).relative?
|
357
|
-
|
358
|
-
_send_file(
|
359
|
-
File.new(path, directory).call(@_env)
|
360
|
-
)
|
361
|
-
end
|
362
|
-
|
363
|
-
# Check if the current request is a HEAD
|
364
|
-
#
|
365
|
-
# @return [TrueClass,FalseClass] the result of the check
|
366
|
-
#
|
367
|
-
# @since 0.3.2
|
368
|
-
def head?
|
369
|
-
request_method == HEAD
|
370
|
-
end
|
371
|
-
|
372
|
-
# NOTE: <tt>Hanami::Action::CSRFProtection</tt> (<tt>hanamirb</tt> gem) depends on this.
|
373
|
-
#
|
374
|
-
# @api private
|
375
|
-
# @since 0.4.4
|
376
|
-
def request_method
|
377
|
-
@_env[REQUEST_METHOD]
|
378
|
-
end
|
379
|
-
|
380
|
-
# @since 1.0.0
|
381
|
-
# @api private
|
382
|
-
def _send_file(response)
|
383
|
-
headers.merge!(response[RESPONSE_HEADERS])
|
384
|
-
|
385
|
-
if response[RESPONSE_CODE] == NOT_FOUND
|
386
|
-
headers.delete(X_CASCADE)
|
387
|
-
headers.delete(CONTENT_LENGTH)
|
388
|
-
halt NOT_FOUND
|
389
|
-
else
|
390
|
-
# FIXME: this is a fix for https://github.com/hanami/controller/issues/240
|
391
|
-
# It's here to maintain the backward compat with 1.1, as we can't remove `#halt`
|
392
|
-
# We should review the workflow for 2.0, because I don't like callbacks to be referenced from here.
|
393
|
-
_run_after_callbacks(params)
|
394
|
-
halt response[RESPONSE_CODE], response[RESPONSE_BODY]
|
395
|
-
end
|
396
|
-
end
|
397
|
-
end
|
398
|
-
end
|
399
|
-
end
|