Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion gem/lib/pagy/modules/abilities/linkable.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require 'cgi/escape'
require 'uri'

class Pagy
Expand Down Expand Up @@ -31,8 +32,13 @@ def build_nested_query(value, prefix = nil)
end
end

# CGI.escape is several times faster than URI.encode_www_form_component and returns the same bytes,
# except for '*' and '~', which are left to URI. URI returns US-ASCII, CGI the input encoding.
def escape(str)
URI.encode_www_form_component(str)
bytes = str.to_s.b
return URI.encode_www_form_component(bytes) if bytes.match?(/[*~]/)

CGI.escape(bytes).force_encoding(Encoding::US_ASCII)
end
end

Expand Down
27 changes: 27 additions & 0 deletions test/unit/pagy/modules/abilities/linkable_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,33 @@
it 'escapes strings' do
_(utils.escape('a b')).must_equal 'a+b'
_(utils.escape('/?&')).must_equal '%2F%3F%26'
_(utils.escape('a*b~c')).must_equal 'a*b%7Ec'
_(utils.escape('café')).must_equal 'caf%C3%A9'
_(utils.escape(:alice)).must_equal 'alice'
_(utils.escape(42)).must_equal '42'
_(utils.escape("\xFF".b)).must_equal '%FF'
end

it 'escapes like URI.encode_www_form_component' do
[nil, '', '*', "a*\xFF", 'é~'.encode('UTF-16LE'), 'é'.encode('ISO-8859-1'), "\0 🙂"].each do |value|
expected = URI.encode_www_form_component(value)
escaped = utils.escape(value)
_(escaped).must_equal expected
_(escaped.encoding).must_equal expected.encoding
end
end

it 'escapes random strings like URI.encode_www_form_component' do
random = Random.new(42)
alphabet = [*'a'..'z', *'0'..'9', *" !\"\#$%&'()*+,-./:;<=>?@[\\]^_`{|}~".chars, 'é', '€', '日', '🙂', "\0"]
strings = Array.new(3000) { Array.new(random.rand(0..12)) { alphabet.sample(random:) }.join }
strings += Array.new(500) { random.bytes(random.rand(0..8)) }
strings.each do |string|
expected = URI.encode_www_form_component(string)
escaped = utils.escape(string)
_(escaped).must_equal expected
_(escaped.encoding).must_equal expected.encoding
end
end

it 'builds simple queries' do
Expand Down
Loading