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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +83 -0
- data/LICENSE.md +1 -1
- data/README.md +299 -537
- data/hanami-controller.gemspec +5 -4
- data/lib/hanami/action/application_action.rb +112 -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/application_configuration.rb +92 -0
- data/lib/hanami/action/base_params.rb +2 -2
- data/lib/hanami/action/cache/cache_control.rb +4 -4
- data/lib/hanami/action/cache/conditional_get.rb +3 -1
- data/lib/hanami/action/cache/directives.rb +1 -1
- data/lib/hanami/action/cache/expires.rb +3 -3
- data/lib/hanami/action/cache.rb +1 -139
- data/lib/hanami/action/configuration.rb +428 -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 +2 -2
- 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 +579 -0
- data/lib/hanami/action/validatable.rb +1 -1
- data/lib/hanami/action/view_name_inferrer.rb +46 -0
- data/lib/hanami/action.rb +129 -73
- data/lib/hanami/controller/version.rb +1 -1
- data/lib/hanami/controller.rb +0 -227
- data/lib/hanami/http/status.rb +2 -2
- metadata +45 -27
- 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/guard.rb +0 -104
- data/lib/hanami/action/exposable.rb +0 -126
- data/lib/hanami/action/head.rb +0 -121
- data/lib/hanami/action/rack/callable.rb +0 -47
- data/lib/hanami/action/rack.rb +0 -399
- data/lib/hanami/action/redirect.rb +0 -59
- data/lib/hanami/action/throwable.rb +0 -196
- data/lib/hanami/controller/configuration.rb +0 -763
- data/lib/hanami-controller.rb +0 -1
data/hanami-controller.gemspec
CHANGED
@@ -17,13 +17,14 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.executables = []
|
18
18
|
spec.test_files = spec.files.grep(%r{^(spec)/})
|
19
19
|
spec.require_paths = ['lib']
|
20
|
-
spec.required_ruby_version = '>= 2.
|
20
|
+
spec.required_ruby_version = '>= 2.6.0'
|
21
21
|
|
22
22
|
spec.add_dependency 'rack', '~> 2.0'
|
23
|
-
spec.add_dependency 'hanami-utils', '~>
|
23
|
+
spec.add_dependency 'hanami-utils', '~> 2.0.alpha'
|
24
|
+
spec.add_dependency 'dry-configurable', '~> 0.13', '>= 0.13.0'
|
24
25
|
|
25
26
|
spec.add_development_dependency 'bundler', '>= 1.6', '< 3'
|
26
27
|
spec.add_development_dependency 'rack-test', '~> 1.0'
|
27
|
-
spec.add_development_dependency 'rake', '~>
|
28
|
-
spec.add_development_dependency 'rspec', '~> 3.
|
28
|
+
spec.add_development_dependency 'rake', '~> 13'
|
29
|
+
spec.add_development_dependency 'rspec', '~> 3.9'
|
29
30
|
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
class Action
|
5
|
+
class ApplicationAction < Module
|
6
|
+
attr_reader :provider
|
7
|
+
attr_reader :application
|
8
|
+
|
9
|
+
def initialize(provider)
|
10
|
+
@provider = provider
|
11
|
+
@application = provider.respond_to?(:application) ? provider.application : Hanami.application
|
12
|
+
end
|
13
|
+
|
14
|
+
def included(action_class)
|
15
|
+
action_class.include InstanceMethods
|
16
|
+
|
17
|
+
define_initialize action_class
|
18
|
+
configure_action action_class
|
19
|
+
extend_behavior action_class
|
20
|
+
end
|
21
|
+
|
22
|
+
def inspect
|
23
|
+
"#<#{self.class.name}[#{provider.name}]>"
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def define_initialize(action_class)
|
29
|
+
resolve_view = method(:resolve_paired_view)
|
30
|
+
resolve_context = method(:resolve_view_context)
|
31
|
+
|
32
|
+
define_method :initialize do |**deps|
|
33
|
+
# Conditionally assign these to repsect any explictly auto-injected
|
34
|
+
# dependencies provided by the class
|
35
|
+
@view ||= deps[:view] || resolve_view.(self.class)
|
36
|
+
@view_context ||= deps[:view_context] || resolve_context.()
|
37
|
+
|
38
|
+
super(**deps)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def resolve_view_context
|
43
|
+
identifier = application.config.actions.view_context_identifier
|
44
|
+
|
45
|
+
if provider.key?(identifier)
|
46
|
+
provider[identifier]
|
47
|
+
elsif application.key?(identifier)
|
48
|
+
application[identifier]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def resolve_paired_view(action_class)
|
53
|
+
view_identifiers = application.config.actions.view_name_inferrer.(
|
54
|
+
action_name: action_class.name,
|
55
|
+
provider: provider
|
56
|
+
)
|
57
|
+
|
58
|
+
view_identifiers.detect { |identifier|
|
59
|
+
break provider[identifier] if provider.key?(identifier)
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
def configure_action(action_class)
|
64
|
+
action_class.config.settings.each do |setting|
|
65
|
+
application_value = application.config.actions.public_send(:"#{setting}")
|
66
|
+
action_class.config.public_send :"#{setting}=", application_value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def extend_behavior(action_class)
|
71
|
+
if application.config.actions.sessions.enabled?
|
72
|
+
require "hanami/action/session"
|
73
|
+
action_class.include Hanami::Action::Session
|
74
|
+
end
|
75
|
+
|
76
|
+
if application.config.actions.csrf_protection
|
77
|
+
require "hanami/action/csrf_protection"
|
78
|
+
action_class.include Hanami::Action::CSRFProtection
|
79
|
+
end
|
80
|
+
|
81
|
+
if application.config.actions.cookies.enabled?
|
82
|
+
require "hanami/action/cookies"
|
83
|
+
action_class.include Hanami::Action::Cookies
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
module InstanceMethods
|
88
|
+
attr_reader :view
|
89
|
+
attr_reader :view_context
|
90
|
+
|
91
|
+
def build_response(**options)
|
92
|
+
options = options.merge(view_options: method(:view_options))
|
93
|
+
super(**options)
|
94
|
+
end
|
95
|
+
|
96
|
+
def view_options(req, res)
|
97
|
+
{context: view_context&.with(**view_context_options(req, res))}.compact
|
98
|
+
end
|
99
|
+
|
100
|
+
def view_context_options(req, res)
|
101
|
+
{request: req, response: res}
|
102
|
+
end
|
103
|
+
|
104
|
+
# Automatically render the view, if the body hasn't been populated yet
|
105
|
+
def finish(req, res, halted)
|
106
|
+
res.render(view, **req.params, **res.exposures) if view && res.body.empty?
|
107
|
+
super
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
class Action
|
5
|
+
class ApplicationConfiguration
|
6
|
+
# Wrapper for application-level configuration of HTTP cookies for Hanami actions.
|
7
|
+
# This decorates the hash of cookie options that is otherwise directly configurable
|
8
|
+
# on actions, and adds the `enabled?` method to allow `ApplicationAction` to
|
9
|
+
# determine whether to include the `Action::Cookies` module.
|
10
|
+
#
|
11
|
+
# @since 2.0.0
|
12
|
+
class Cookies
|
13
|
+
attr_reader :options
|
14
|
+
|
15
|
+
def initialize(options)
|
16
|
+
@options = options
|
17
|
+
end
|
18
|
+
|
19
|
+
def enabled?
|
20
|
+
!options.nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_h
|
24
|
+
options.to_h
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/constants"
|
4
|
+
require "hanami/utils/string"
|
5
|
+
require "hanami/utils/class"
|
6
|
+
|
7
|
+
module Hanami
|
8
|
+
class Action
|
9
|
+
class ApplicationConfiguration
|
10
|
+
# Configuration for HTTP sessions in Hanami actions
|
11
|
+
#
|
12
|
+
# @since 2.0.0
|
13
|
+
class Sessions
|
14
|
+
attr_reader :storage, :options
|
15
|
+
|
16
|
+
def initialize(storage = nil, *options)
|
17
|
+
@storage = storage
|
18
|
+
@options = options
|
19
|
+
end
|
20
|
+
|
21
|
+
def enabled?
|
22
|
+
!storage.nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
def middleware
|
26
|
+
return [] if !enabled?
|
27
|
+
|
28
|
+
[[storage_middleware, options]]
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def storage_middleware
|
34
|
+
require_storage
|
35
|
+
|
36
|
+
name = Utils::String.classify(storage)
|
37
|
+
Utils::Class.load!(name, ::Rack::Session)
|
38
|
+
end
|
39
|
+
|
40
|
+
def require_storage
|
41
|
+
require "rack/session/#{storage}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "application_configuration/cookies"
|
4
|
+
require_relative "application_configuration/sessions"
|
5
|
+
require_relative "configuration"
|
6
|
+
require_relative "view_name_inferrer"
|
7
|
+
|
8
|
+
module Hanami
|
9
|
+
class Action
|
10
|
+
class ApplicationConfiguration
|
11
|
+
include Dry::Configurable
|
12
|
+
|
13
|
+
setting :cookies, default: {}, constructor: -> options { Cookies.new(options) }
|
14
|
+
setting :sessions, constructor: proc { |storage, *options| Sessions.new(storage, *options) }
|
15
|
+
setting :csrf_protection
|
16
|
+
|
17
|
+
setting :name_inference_base, default: "actions"
|
18
|
+
setting :view_context_identifier, default: "view.context"
|
19
|
+
setting :view_name_inferrer, default: ViewNameInferrer
|
20
|
+
setting :view_name_inference_base, default: "views"
|
21
|
+
|
22
|
+
def initialize(*)
|
23
|
+
super
|
24
|
+
|
25
|
+
@base_configuration = Configuration.new
|
26
|
+
|
27
|
+
configure_defaults
|
28
|
+
end
|
29
|
+
|
30
|
+
def finalize!
|
31
|
+
# A nil value for `csrf_protection` means it has not been explicitly configured
|
32
|
+
# (neither true nor false), so we can default it to whether sessions are enabled
|
33
|
+
self.csrf_protection = sessions.enabled? if csrf_protection.nil?
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the list of available settings
|
37
|
+
#
|
38
|
+
# @return [Set]
|
39
|
+
#
|
40
|
+
# @since 2.0.0
|
41
|
+
# @api private
|
42
|
+
def settings
|
43
|
+
base_configuration.settings + self.class.settings
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
attr_reader :base_configuration
|
49
|
+
|
50
|
+
# Apply defaults for base configuration settings
|
51
|
+
def configure_defaults
|
52
|
+
self.default_request_format = :html
|
53
|
+
self.default_response_format = :html
|
54
|
+
|
55
|
+
self.default_headers = {
|
56
|
+
"X-Frame-Options" => "DENY",
|
57
|
+
"X-Content-Type-Options" => "nosniff",
|
58
|
+
"X-XSS-Protection" => "1; mode=block",
|
59
|
+
"Content-Security-Policy" => \
|
60
|
+
"base-uri 'self'; " \
|
61
|
+
"child-src 'self'; " \
|
62
|
+
"connect-src 'self'; " \
|
63
|
+
"default-src 'none'; " \
|
64
|
+
"font-src 'self'; " \
|
65
|
+
"form-action 'self'; " \
|
66
|
+
"frame-ancestors 'self'; " \
|
67
|
+
"frame-src 'self'; " \
|
68
|
+
"img-src 'self' https: data:; " \
|
69
|
+
"media-src 'self'; " \
|
70
|
+
"object-src 'none'; " \
|
71
|
+
"plugin-types application/pdf; " \
|
72
|
+
"script-src 'self'; " \
|
73
|
+
"style-src 'self' 'unsafe-inline' https:"
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def method_missing(name, *args, &block)
|
78
|
+
if config.respond_to?(name)
|
79
|
+
config.public_send(name, *args, &block)
|
80
|
+
elsif base_configuration.respond_to?(name)
|
81
|
+
base_configuration.public_send(name, *args, &block)
|
82
|
+
else
|
83
|
+
super
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def respond_to_missing?(name, _incude_all = false)
|
88
|
+
config.respond_to?(name) || base_configuration.respond_to?(name) || super
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -2,7 +2,7 @@ require 'rack/request'
|
|
2
2
|
require 'hanami/utils/hash'
|
3
3
|
|
4
4
|
module Hanami
|
5
|
-
|
5
|
+
class Action
|
6
6
|
class BaseParams
|
7
7
|
# The key that returns raw input from the Rack env
|
8
8
|
#
|
@@ -173,7 +173,7 @@ module Hanami
|
|
173
173
|
def _router_params(fallback = {})
|
174
174
|
env.fetch(ROUTER_PARAMS) do
|
175
175
|
if session = fallback.delete(RACK_SESSION) # rubocop:disable Lint/AssignmentInCondition
|
176
|
-
fallback[RACK_SESSION] = Utils::Hash.
|
176
|
+
fallback[RACK_SESSION] = Utils::Hash.deep_symbolize(session)
|
177
177
|
end
|
178
178
|
|
179
179
|
fallback
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'hanami/action/cache/directives'
|
2
2
|
|
3
3
|
module Hanami
|
4
|
-
|
4
|
+
class Action
|
5
5
|
module Cache
|
6
6
|
# Module with Cache-Control logic
|
7
7
|
#
|
@@ -37,7 +37,7 @@ module Hanami
|
|
37
37
|
def cache_control_directives
|
38
38
|
@cache_control_directives || Object.new.tap do |null_object|
|
39
39
|
def null_object.headers
|
40
|
-
Hash.new
|
40
|
+
::Hash.new
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
@@ -49,9 +49,9 @@ module Hanami
|
|
49
49
|
# @api private
|
50
50
|
#
|
51
51
|
# @see Hanami::Action#finish
|
52
|
-
def finish
|
52
|
+
def finish(_, res, _)
|
53
|
+
res.headers.merge!(self.class.cache_control_directives.headers) unless res.headers.include? HEADER
|
53
54
|
super
|
54
|
-
headers.merge!(self.class.cache_control_directives.headers) unless headers.include? HEADER
|
55
55
|
end
|
56
56
|
|
57
57
|
# Class which stores CacheControl values
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'hanami/action/cache/cache_control'
|
2
2
|
|
3
3
|
module Hanami
|
4
|
-
|
4
|
+
class Action
|
5
5
|
module Cache
|
6
6
|
# Module with Expires logic
|
7
7
|
#
|
@@ -49,9 +49,9 @@ module Hanami
|
|
49
49
|
# @api private
|
50
50
|
#
|
51
51
|
# @see Hanami::Action#finish
|
52
|
-
def finish
|
52
|
+
def finish(_, res, _)
|
53
|
+
res.headers.merge!(self.class.expires_directives.headers) unless res.headers.include? HEADER
|
53
54
|
super
|
54
|
-
headers.merge!(self.class.expires_directives.headers) unless headers.include? HEADER
|
55
55
|
end
|
56
56
|
|
57
57
|
# Class which stores Expires directives
|
data/lib/hanami/action/cache.rb
CHANGED
@@ -3,7 +3,7 @@ require 'hanami/action/cache/expires'
|
|
3
3
|
require 'hanami/action/cache/conditional_get'
|
4
4
|
|
5
5
|
module Hanami
|
6
|
-
|
6
|
+
class Action
|
7
7
|
# Cache type API
|
8
8
|
#
|
9
9
|
# @since 0.3.0
|
@@ -26,144 +26,6 @@ module Hanami
|
|
26
26
|
include CacheControl, Expires
|
27
27
|
end
|
28
28
|
end
|
29
|
-
|
30
|
-
protected
|
31
|
-
|
32
|
-
# Specify response freshness policy for HTTP caches (Cache-Control header).
|
33
|
-
# Any number of non-value directives (:public, :private, :no_cache,
|
34
|
-
# :no_store, :must_revalidate, :proxy_revalidate) may be passed along with
|
35
|
-
# a Hash of value directives (:max_age, :min_stale, :s_max_age).
|
36
|
-
#
|
37
|
-
# See RFC 2616 / 14.9 for more on standard cache control directives:
|
38
|
-
# http://tools.ietf.org/html/rfc2616#section-14.9.1
|
39
|
-
#
|
40
|
-
# @param values [Array<Symbols, Hash>] mapped to cache_control directives
|
41
|
-
# @option values [Symbol] :public
|
42
|
-
# @option values [Symbol] :private
|
43
|
-
# @option values [Symbol] :no_cache
|
44
|
-
# @option values [Symbol] :no_store
|
45
|
-
# @option values [Symbol] :must_validate
|
46
|
-
# @option values [Symbol] :proxy_revalidate
|
47
|
-
# @option values [Hash] :max_age
|
48
|
-
# @option values [Hash] :min_stale
|
49
|
-
# @option values [Hash] :s_max_age
|
50
|
-
#
|
51
|
-
# @return void
|
52
|
-
#
|
53
|
-
# @since 0.3.0
|
54
|
-
#
|
55
|
-
# @example
|
56
|
-
# require 'hanami/controller'
|
57
|
-
# require 'hanami/action/cache'
|
58
|
-
#
|
59
|
-
# class Show
|
60
|
-
# include Hanami::Action
|
61
|
-
# include Hanami::Action::Cache
|
62
|
-
#
|
63
|
-
# def call(params)
|
64
|
-
# # ...
|
65
|
-
#
|
66
|
-
# # set Cache-Control directives
|
67
|
-
# cache_control :public, max_age: 900, s_maxage: 86400
|
68
|
-
#
|
69
|
-
# # overwrite previous Cache-Control directives
|
70
|
-
# cache_control :private, :no_cache, :no_store
|
71
|
-
#
|
72
|
-
# => Cache-Control: private, no-store, max-age=900
|
73
|
-
#
|
74
|
-
# end
|
75
|
-
# end
|
76
|
-
def cache_control(*values)
|
77
|
-
cache_control = CacheControl::Directives.new(*values)
|
78
|
-
headers.merge!(cache_control.headers)
|
79
|
-
end
|
80
|
-
|
81
|
-
# Set the Expires header and Cache-Control/max-age directive. Amount
|
82
|
-
# can be an integer number of seconds in the future or a Time object
|
83
|
-
# indicating when the response should be considered "stale". The remaining
|
84
|
-
# "values" arguments are passed to the #cache_control helper:
|
85
|
-
#
|
86
|
-
# @param amount [Integer,Time] number of seconds or point in time
|
87
|
-
# @param values [Array<Symbols>] mapped to cache_control directives
|
88
|
-
#
|
89
|
-
# @return void
|
90
|
-
#
|
91
|
-
# @since 0.3.0
|
92
|
-
#
|
93
|
-
# @example
|
94
|
-
# require 'hanami/controller'
|
95
|
-
# require 'hanami/action/cache'
|
96
|
-
#
|
97
|
-
# class Show
|
98
|
-
# include Hanami::Action
|
99
|
-
# include Hanami::Action::Cache
|
100
|
-
#
|
101
|
-
# def call(params)
|
102
|
-
# # ...
|
103
|
-
#
|
104
|
-
# # set Cache-Control directives and Expires
|
105
|
-
# expires 900, :public
|
106
|
-
#
|
107
|
-
# # overwrite Cache-Control directives and Expires
|
108
|
-
# expires 300, :private, :no_cache, :no_store
|
109
|
-
#
|
110
|
-
# => Expires: Thu, 26 Jun 2014 12:00:00 GMT
|
111
|
-
# => Cache-Control: private, no-cache, no-store max-age=300
|
112
|
-
#
|
113
|
-
# end
|
114
|
-
# end
|
115
|
-
def expires(amount, *values)
|
116
|
-
expires = Expires::Directives.new(amount, *values)
|
117
|
-
headers.merge!(expires.headers)
|
118
|
-
end
|
119
|
-
|
120
|
-
# Set the etag, last_modified, or both headers on the response
|
121
|
-
# and halts a 304 Not Modified if the request is still fresh
|
122
|
-
# respecting IfNoneMatch and IfModifiedSince request headers
|
123
|
-
#
|
124
|
-
# @param options [Hash]
|
125
|
-
# @option options [Integer] :etag for testing IfNoneMatch conditions
|
126
|
-
# @option options [Date] :last_modified for testing IfModifiedSince conditions
|
127
|
-
#
|
128
|
-
# @return void
|
129
|
-
#
|
130
|
-
# @since 0.3.0
|
131
|
-
#
|
132
|
-
# @example
|
133
|
-
# require 'hanami/controller'
|
134
|
-
# require 'hanami/action/cache'
|
135
|
-
#
|
136
|
-
# class Show
|
137
|
-
# include Hanami::Action
|
138
|
-
# include Hanami::Action::Cache
|
139
|
-
#
|
140
|
-
# def call(params)
|
141
|
-
# # ...
|
142
|
-
#
|
143
|
-
# # set etag response header and halt 304
|
144
|
-
# # if request matches IF_NONE_MATCH header
|
145
|
-
# fresh etag: @resource.updated_at.to_i
|
146
|
-
#
|
147
|
-
# # set last_modified response header and halt 304
|
148
|
-
# # if request matches IF_MODIFIED_SINCE
|
149
|
-
# fresh last_modified: @resource.updated_at
|
150
|
-
#
|
151
|
-
# # set etag and last_modified response header,
|
152
|
-
# # halt 304 if request matches IF_MODIFIED_SINCE
|
153
|
-
# # and IF_NONE_MATCH
|
154
|
-
# fresh last_modified: @resource.updated_at
|
155
|
-
#
|
156
|
-
# end
|
157
|
-
# end
|
158
|
-
def fresh(options)
|
159
|
-
conditional_get = ConditionalGet.new(@_env, options)
|
160
|
-
|
161
|
-
headers.merge!(conditional_get.headers)
|
162
|
-
|
163
|
-
conditional_get.fresh? do
|
164
|
-
halt 304
|
165
|
-
end
|
166
|
-
end
|
167
29
|
end
|
168
30
|
end
|
169
31
|
end
|