Skip to content

Commit 6e6958f

Browse files
authored
Update normalize_path like Rails (#2536)
* Update normalize_path like rails * Fix cop * Add CHANGELOG.md * Add specs and comment
1 parent 48ebc25 commit 6e6958f

File tree

3 files changed

+79
-3
lines changed

3 files changed

+79
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* [#2532](https://github.com/ruby-grape/grape/pull/2532): Update RuboCop 1.71.2 - [@ericproulx](https://github.com/ericproulx).
66
* [#2535](https://github.com/ruby-grape/grape/pull/2535): Delegates calls to inner objects - [@ericproulx](https://github.com/ericproulx).
77
* [#2537](https://github.com/ruby-grape/grape/pull/2537): Use activesupport `try` pattern - [@ericproulx](https://github.com/ericproulx).
8+
* [#2536](https://github.com/ruby-grape/grape/pull/2536): Update normalize_path like Rails - [@ericproulx](https://github.com/ericproulx).
89
* Your contribution here.
910

1011
#### Fixes

lib/grape/router.rb

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,30 @@ module Grape
44
class Router
55
attr_reader :map, :compiled
66

7+
# Taken from Rails
8+
# normalize_path("/foo") # => "/foo"
9+
# normalize_path("/foo/") # => "/foo"
10+
# normalize_path("foo") # => "/foo"
11+
# normalize_path("") # => "/"
12+
# normalize_path("/%ab") # => "/%AB"
13+
# https://github.com/rails/rails/blob/00cc4ff0259c0185fe08baadaa40e63ea2534f6e/actionpack/lib/action_dispatch/journey/router/utils.rb#L19
714
def self.normalize_path(path)
15+
return +'/' unless path
16+
17+
# Fast path for the overwhelming majority of paths that don't need to be normalized
18+
return path.dup if path == '/' || (path.start_with?('/') && !(path.end_with?('/') || path.match?(%r{%|//})))
19+
20+
# Slow path
21+
encoding = path.encoding
822
path = +"/#{path}"
923
path.squeeze!('/')
10-
path.sub!(%r{/+\Z}, '')
11-
path = '/' if path == ''
12-
path
24+
25+
unless path == '/'
26+
path.delete_suffix!('/')
27+
path.gsub!(/(%[a-f0-9]{2})/) { ::Regexp.last_match(1).upcase }
28+
end
29+
30+
path.force_encoding(encoding)
1331
end
1432

1533
def initialize

spec/grape/router_spec.rb

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# frozen_string_literal: true
2+
3+
describe Grape::Router do
4+
describe '.normalize_path' do
5+
subject { described_class.normalize_path(path) }
6+
7+
context 'when no leading slash' do
8+
let(:path) { 'foo%20bar%20baz' }
9+
10+
it { is_expected.to eq '/foo%20bar%20baz' }
11+
end
12+
13+
context 'when path ends with slash' do
14+
let(:path) { '/foo%20bar%20baz/' }
15+
16+
it { is_expected.to eq '/foo%20bar%20baz' }
17+
end
18+
19+
context 'when path has recurring slashes' do
20+
let(:path) { '////foo%20bar%20baz' }
21+
22+
it { is_expected.to eq '/foo%20bar%20baz' }
23+
end
24+
25+
context 'when not greedy' do
26+
let(:path) { '/foo%20bar%20baz' }
27+
28+
it { is_expected.to eq '/foo%20bar%20baz' }
29+
end
30+
31+
context 'when encoded string in lowercase' do
32+
let(:path) { '/foo%aabar%aabaz' }
33+
34+
it { is_expected.to eq '/foo%AAbar%AAbaz' }
35+
end
36+
37+
context 'when nil' do
38+
let(:path) { nil }
39+
40+
it { is_expected.to eq '/' }
41+
end
42+
43+
context 'when empty string' do
44+
let(:path) { '' }
45+
46+
it { is_expected.to eq '/' }
47+
end
48+
49+
context 'when encoding is different' do
50+
subject { described_class.normalize_path(path).encoding }
51+
52+
let(:path) { '/foo%AAbar%AAbaz'.b }
53+
54+
it { is_expected.to eq(Encoding::BINARY) }
55+
end
56+
end
57+
end

0 commit comments

Comments
 (0)