Skip to content

Commit 9931f9a

Browse files
committed
Delegate cookies management to Grape::Request
Lazy read cookies
1 parent 4f6725a commit 9931f9a

File tree

5 files changed

+56
-69
lines changed

5 files changed

+56
-69
lines changed

lib/grape/cookies.rb

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,51 @@
22

33
module Grape
44
class Cookies
5-
def initialize
6-
@cookies = {}
7-
@send_cookies = {}
8-
end
5+
extend Forwardable
96

10-
def read(request)
11-
request.cookies.each do |name, value|
12-
@cookies[name.to_s] = value
13-
end
7+
DELETED_COOKIES_ATTRS = {
8+
max_age: '0',
9+
value: '',
10+
expires: Time.at(0)
11+
}.freeze
12+
13+
def_delegators :cookies, :[], :each
14+
15+
def initialize(lazy_rack_cookies)
16+
@lazy_cookies = lazy_rack_cookies
1417
end
1518

16-
def write(header)
17-
@cookies.select { |key, _value| @send_cookies[key] == true }.each do |name, value|
18-
cookie_value = value.is_a?(Hash) ? value : { value: value }
19-
Rack::Utils.set_cookie_header! header, name, cookie_value
19+
def each_response_cookies
20+
return unless defined?(@send_cookies)
21+
22+
send_cookies.each do |name|
23+
yield name, cookies[name]
2024
end
2125
end
2226

23-
def [](name)
24-
@cookies[name.to_s]
27+
def []=(name, value)
28+
cookies[name] = value
29+
send_cookies << name
2530
end
2631

27-
def []=(name, value)
28-
@cookies[name.to_s] = value
29-
@send_cookies[name.to_s] = true
32+
# see https://github.com/rack/rack/blob/main/lib/rack/utils.rb#L338-L340
33+
def delete(name, **opts)
34+
self.[]=(name, opts.merge(DELETED_COOKIES_ATTRS))
3035
end
3136

32-
def each(&block)
33-
@cookies.each(&block)
37+
private
38+
39+
def cookies
40+
return @cookies if defined?(@cookies)
41+
42+
# we don't want read cookies from rack if it has never been called
43+
@cookies = @lazy_cookies.call.with_indifferent_access
44+
@lazy_cookies = nil
45+
@cookies
3446
end
3547

36-
# see https://github.com/rack/rack/blob/main/lib/rack/utils.rb#L338-L340
37-
def delete(name, **opts)
38-
options = opts.merge(max_age: '0', value: '', expires: Time.at(0))
39-
self.[]=(name, options)
48+
def send_cookies
49+
@send_cookies ||= Set.new
4050
end
4151
end
4252
end

lib/grape/dsl/inside_route.rb

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -257,18 +257,6 @@ def content_type(val = nil)
257257
end
258258
end
259259

260-
# Set or get a cookie
261-
#
262-
# @example
263-
# cookies[:mycookie] = 'mycookie val'
264-
# cookies['mycookie-string'] = 'mycookie string val'
265-
# cookies[:more] = { value: '123', expires: Time.at(0) }
266-
# cookies.delete :more
267-
#
268-
def cookies
269-
@cookies ||= Cookies.new
270-
end
271-
272260
# Allows you to define the response body as something other than the
273261
# return value.
274262
#

lib/grape/endpoint.rb

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class Endpoint
1313
attr_accessor :block, :source, :options
1414
attr_reader :env, :request
1515

16-
def_delegators :request, :params, :headers
16+
def_delegators :request, :params, :headers, :cookies
1717

1818
class << self
1919
def new(...)
@@ -164,10 +164,9 @@ def mount_in(router)
164164

165165
def to_routes
166166
default_route_options = prepare_default_route_attributes
167-
default_path_settings = prepare_default_path_settings
168167

169168
map_routes do |method, raw_path|
170-
prepared_path = Path.new(raw_path, namespace, default_path_settings)
169+
prepared_path = Path.new(raw_path, namespace, prepare_default_path_settings)
171170
params = options[:route_options].present? ? options[:route_options].merge(default_route_options) : default_route_options
172171
route = Grape::Router::Route.new(method, prepared_path.origin, prepared_path.suffix, params)
173172
route.apply(self)
@@ -248,18 +247,16 @@ def inspect
248247

249248
def run
250249
ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self, env: env) do
251-
@header = Grape::Util::Header.new
252250
@request = Grape::Request.new(env, build_params_with: namespace_inheritable(:build_params_with))
253251
begin
254-
cookies.read(@request)
255252
self.class.run_before_each(self)
256253
run_filters befores, :before
257254

258-
if (allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS])
259-
allow_header_value = allowed_methods.join(', ')
260-
raise Grape::Exceptions::MethodNotAllowed.new(header.merge('Allow' => allow_header_value)) unless options?
255+
if env.key?(Grape::Env::GRAPE_ALLOWED_METHODS)
256+
header['Allow'] = env[Grape::Env::GRAPE_ALLOWED_METHODS].join(', ')
257+
raise Grape::Exceptions::MethodNotAllowed.new(header) unless options?
261258

262-
header Grape::Http::Headers::ALLOW, allow_header_value
259+
header Grape::Http::Headers::ALLOW, header['Allow']
263260
response_object = ''
264261
status 204
265262
else
@@ -270,7 +267,7 @@ def run
270267
end
271268

272269
run_filters afters, :after
273-
cookies.write(header)
270+
build_response_cookies
274271

275272
# status verifies body presence when DELETE
276273
@body ||= response_object
@@ -332,24 +329,10 @@ def run_filters(filters, type = :other)
332329
extend post_extension if post_extension
333330
end
334331

335-
def befores
336-
namespace_stackable(:befores)
337-
end
338-
339-
def before_validations
340-
namespace_stackable(:before_validations)
341-
end
342-
343-
def after_validations
344-
namespace_stackable(:after_validations)
345-
end
346-
347-
def afters
348-
namespace_stackable(:afters)
349-
end
350-
351-
def finallies
352-
namespace_stackable(:finallies)
332+
%i(befores before_validations after_validations afters finallies).each do |method|
333+
define_method method do
334+
namespace_stackable(method)
335+
end
353336
end
354337

355338
def validations
@@ -417,5 +400,12 @@ def build_helpers
417400

418401
Module.new { helpers.each { |mod_to_include| include mod_to_include } }
419402
end
403+
404+
def build_response_cookies
405+
cookies.each_response_cookies do |name, value|
406+
cookie_value = value.is_a?(Hash) ? value : { value: value }
407+
Rack::Utils.set_cookie_header! header, name, cookie_value
408+
end
409+
end
420410
end
421411
end

lib/grape/request.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class Request < Rack::Request
66
HTTP_PREFIX = 'HTTP_'
77

88
alias rack_params params
9+
alias rack_cookies cookies
910

1011
def initialize(env, build_params_with: nil)
1112
super(env)
@@ -20,6 +21,10 @@ def headers
2021
@headers ||= build_headers
2122
end
2223

24+
def cookies
25+
@cookies ||= Grape::Cookies.new(-> { rack_cookies })
26+
end
27+
2328
# needs to be public until extensions param_builder are removed
2429
def grape_routing_args
2530
# preserve version from query string parameters

spec/grape/dsl/inside_route_spec.rb

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,6 @@ def initialize
165165
end
166166
end
167167

168-
describe '#cookies' do
169-
it 'returns an instance of Cookies' do
170-
expect(subject.cookies).to be_a Grape::Cookies
171-
end
172-
end
173-
174168
describe '#body' do
175169
describe 'set' do
176170
before do

0 commit comments

Comments
 (0)