Skip to content

Commit 809add4

Browse files
authored
Delegate cookies management to Grape::Request (#2549)
1 parent 5bb4c58 commit 809add4

File tree

6 files changed

+56
-69
lines changed

6 files changed

+56
-69
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* [#2536](https://github.com/ruby-grape/grape/pull/2536): Update normalize_path like Rails - [@ericproulx](https://github.com/ericproulx).
99
* [#2540](https://github.com/ruby-grape/grape/pull/2540): Introduce Params builder with symbolized short name - [@ericproulx](https://github.com/ericproulx).
1010
* [#2550](https://github.com/ruby-grape/grape/pull/2550): Drop ActiveSupport 6.0 - [@ericproulx](https://github.com/ericproulx).
11+
* [#2549](https://github.com/ruby-grape/grape/pull/2549): Delegate cookies management to `Grape::Request` - [@ericproulx](https://github.com/ericproulx).
1112
* Your contribution here.
1213

1314
#### Fixes

lib/grape/cookies.rb

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,49 @@
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(rack_cookies)
16+
@cookies = rack_cookies
17+
@send_cookies = nil
1418
end
1519

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
20+
def response_cookies
21+
return unless @send_cookies
22+
23+
send_cookies.each do |name|
24+
yield name, cookies[name]
2025
end
2126
end
2227

23-
def [](name)
24-
@cookies[name.to_s]
28+
def []=(name, value)
29+
cookies[name] = value
30+
send_cookies << name
2531
end
2632

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

32-
def each(&block)
33-
@cookies.each(&block)
38+
private
39+
40+
def cookies
41+
return @cookies unless @cookies.is_a?(Proc)
42+
43+
@cookies = @cookies.call.with_indifferent_access
3444
end
3545

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)
46+
def send_cookies
47+
@send_cookies ||= Set.new
4048
end
4149
end
4250
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: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ 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
17+
def_delegator :cookies, :response_cookies
1718

1819
class << self
1920
def new(...)
@@ -164,10 +165,9 @@ def mount_in(router)
164165

165166
def to_routes
166167
default_route_options = prepare_default_route_attributes
167-
default_path_settings = prepare_default_path_settings
168168

169169
map_routes do |method, raw_path|
170-
prepared_path = Path.new(raw_path, namespace, default_path_settings)
170+
prepared_path = Path.new(raw_path, namespace, prepare_default_path_settings)
171171
params = options[:route_options].present? ? options[:route_options].merge(default_route_options) : default_route_options
172172
route = Grape::Router::Route.new(method, prepared_path.origin, prepared_path.suffix, params)
173173
route.apply(self)
@@ -248,18 +248,16 @@ def inspect
248248

249249
def run
250250
ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self, env: env) do
251-
@header = Grape::Util::Header.new
252251
@request = Grape::Request.new(env, build_params_with: namespace_inheritable(:build_params_with))
253252
begin
254-
cookies.read(@request)
255253
self.class.run_before_each(self)
256254
run_filters befores, :before
257255

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?
256+
if env.key?(Grape::Env::GRAPE_ALLOWED_METHODS)
257+
header['Allow'] = env[Grape::Env::GRAPE_ALLOWED_METHODS].join(', ')
258+
raise Grape::Exceptions::MethodNotAllowed.new(header) unless options?
261259

262-
header Grape::Http::Headers::ALLOW, allow_header_value
260+
header Grape::Http::Headers::ALLOW, header['Allow']
263261
response_object = ''
264262
status 204
265263
else
@@ -270,7 +268,7 @@ def run
270268
end
271269

272270
run_filters afters, :after
273-
cookies.write(header)
271+
build_response_cookies
274272

275273
# status verifies body presence when DELETE
276274
@body ||= response_object
@@ -332,24 +330,10 @@ def run_filters(filters, type = :other)
332330
extend post_extension if post_extension
333331
end
334332

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)
333+
%i[befores before_validations after_validations afters finallies].each do |method|
334+
define_method method do
335+
namespace_stackable(method)
336+
end
353337
end
354338

355339
def validations
@@ -417,5 +401,12 @@ def build_helpers
417401

418402
Module.new { helpers.each { |mod_to_include| include mod_to_include } }
419403
end
404+
405+
def build_response_cookies
406+
response_cookies do |name, value|
407+
cookie_value = value.is_a?(Hash) ? value : { value: value }
408+
Rack::Utils.set_cookie_header! header, name, cookie_value
409+
end
410+
end
420411
end
421412
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)