Downcase encoded slug by default and more specs

This commit is contained in:
Erick Guan 2017-10-03 12:52:24 +02:00 committed by Guo Xiang Tan
parent c650ef9138
commit 7c3123a2dd
3 changed files with 68 additions and 28 deletions

View File

@ -3,8 +3,9 @@
module Slug module Slug
CHAR_FILTER_REGEXP = /[:\/\?#\[\]@!\$&'\(\)\*\+,;=_\.~%\\`^\s|\{\}"<>]+/ # :/?#[]@!$&'()*+,;=_.~%\`^|{}"<> CHAR_FILTER_REGEXP = /[:\/\?#\[\]@!\$&'\(\)\*\+,;=_\.~%\\`^\s|\{\}"<>]+/ # :/?#[]@!$&'()*+,;=_.~%\`^|{}"<>
MAX_LENGTH = 255
def self.for(string, default = 'topic') def self.for(string, default = 'topic', max_length = MAX_LENGTH)
slug = slug =
case (SiteSetting.slug_generation_method || :ascii).to_sym case (SiteSetting.slug_generation_method || :ascii).to_sym
when :ascii then self.ascii_generator(string) when :ascii then self.ascii_generator(string)
@ -13,30 +14,38 @@ module Slug
end end
# Reject slugs that only contain numbers, because they would be indistinguishable from id's. # Reject slugs that only contain numbers, because they would be indistinguishable from id's.
slug = (slug =~ /[^\d]/ ? slug : '') slug = (slug =~ /[^\d]/ ? slug : '')
slug = self.prettify_slug(slug, max_length: max_length)
slug.blank? ? default : slug slug.blank? ? default : slug
end end
def self.sanitize(string) def self.sanitize(string, downcase: false, max_length: MAX_LENGTH)
self.encoded_generator(string) slug = self.encoded_generator(string, downcase: downcase)
self.prettify_slug(slug, max_length: max_length)
end end
private private
def self.ascii_generator(string) def self.prettify_slug(slug, max_length: MAX_LENGTH)
string.tr("'", "") slug.tr!("_", "-")
.parameterize slug = slug.squeeze('-') # squeeze continuous dashes to prettify slug
.tr("_", "-") slug.gsub!(/\A-+|-+\z/, '') # remove possible trailing and preceding dashes
slug.truncate(max_length, omission: '')
end end
def self.encoded_generator(string) def self.ascii_generator(string)
string.tr!("'", "")
string.parameterize
end
def self.encoded_generator(string, downcase: true)
# This generator will sanitize almost all special characters, # This generator will sanitize almost all special characters,
# including reserved characters from RFC3986. # including reserved characters from RFC3986.
# See also URI::REGEXP::PATTERN. # See also URI::REGEXP::PATTERN.
string.strip string.strip!
.gsub(/\s+/, '-') string.gsub!(/\s+/, '-')
.gsub(CHAR_FILTER_REGEXP, '') string.gsub!(CHAR_FILTER_REGEXP, '')
.gsub(/\A-+|-+\z/, '') # remove possible trailing and preceding dashes string.downcase! if downcase
.squeeze('-') # squeeze continuous dashes to prettify slug string
end end
def self.none_generator(string) def self.none_generator(string)

View File

@ -6,6 +6,24 @@ require 'slug'
describe Slug do describe Slug do
describe '#for' do describe '#for' do
let(:default_slug) { 'topic' }
let(:very_long_string) do
'内容似乎不清晰,这是个完整的句子吗?内容似乎不清晰,这是个完整的句子吗?' * 10
end
it 'returns topic by default' do
expect(Slug.for('')).to eq default_slug
end
it 'accepts fallback' do
expect(Slug.for('', 'king')).to eq 'king'
end
it 'replaces the underscore' do
expect(Slug.for("o_o_o")).to eq("o-o-o")
end
context 'ascii generator' do context 'ascii generator' do
before { SiteSetting.slug_generation_method = 'ascii' } before { SiteSetting.slug_generation_method = 'ascii' }
@ -14,11 +32,15 @@ describe Slug do
end end
it 'generates default slug when nothing' do it 'generates default slug when nothing' do
expect(Slug.for('')).to eq('topic') expect(Slug.for('')).to eq(default_slug)
end end
it "doesn't generate slugs that are just numbers" do it "doesn't generate slugs that are just numbers" do
expect(Slug.for('123')).to eq('topic') expect(Slug.for('123')).to eq(default_slug)
end
it "fallbacks to empty string if it's too long" do
expect(Slug.for(very_long_string)).to eq(default_slug)
end end
end end
@ -28,14 +50,25 @@ describe Slug do
it 'generates the slug' do it 'generates the slug' do
expect(Slug.for("熱帶風暴畫眉")).to eq('熱帶風暴畫眉') expect(Slug.for("熱帶風暴畫眉")).to eq('熱帶風暴畫眉')
expect(Slug.for("Jeff hate's !~-_|,=#this")).to eq("jeff-hates-this")
end end
it 'generates default slug when nothing' do it 'generates default slug when nothing' do
expect(Slug.for('')).to eq('topic') expect(Slug.for('')).to eq(default_slug)
end end
it "doesn't generate slugs that are just numbers" do it "doesn't generate slugs that are just numbers" do
expect(Slug.for('123')).to eq('topic') expect(Slug.for('123')).to eq(default_slug)
end
it "handles the special characters" do
expect(Slug.for(
" - English and Chinese title with special characters / 中文标题 !@:?\\:'`#^& $%&*()` -- "
)).to eq("english-and-chinese-title-with-special-characters-中文标题")
end
it "kills the trailing dash" do
expect(Slug.for("2- -this!~-_|,we-#-=^-")).to eq('2-this-we')
end end
end end
@ -45,9 +78,9 @@ describe Slug do
it 'generates the slug' do it 'generates the slug' do
expect(Slug.for("hello world", 'category')).to eq('category') expect(Slug.for("hello world", 'category')).to eq('category')
expect(Slug.for("hello world")).to eq('topic') expect(Slug.for("hello world")).to eq(default_slug)
expect(Slug.for('')).to eq('topic') expect(Slug.for('')).to eq(default_slug)
expect(Slug.for('123')).to eq('topic') expect(Slug.for('123')).to eq(default_slug)
end end
end end
end end
@ -89,10 +122,6 @@ describe Slug do
expect(Slug.ascii_generator(from)).to eq(to) expect(Slug.ascii_generator(from)).to eq(to)
end end
it 'replaces underscores' do
expect(Slug.ascii_generator("o_o_o")).to eq("o-o-o")
end
it "doesn't keep single quotes within word" do it "doesn't keep single quotes within word" do
expect(Slug.ascii_generator("Jeff hate's this")).to eq("jeff-hates-this") expect(Slug.ascii_generator("Jeff hate's this")).to eq("jeff-hates-this")
end end
@ -111,15 +140,13 @@ describe Slug do
after { SiteSetting.slug_generation_method = 'ascii' } after { SiteSetting.slug_generation_method = 'ascii' }
it 'generates precentage encoded string' do it 'generates precentage encoded string' do
expect(Slug.encoded_generator("Jeff hate's !~-_|,=#this")).to eq("Jeff-hates-this")
expect(Slug.encoded_generator("뉴스피드")).to eq("뉴스피드") expect(Slug.encoded_generator("뉴스피드")).to eq("뉴스피드")
expect(Slug.encoded_generator("آموزش اضافه کردن لینک اختیاری به هدر")).to eq("آموزش-اضافه-کردن-لینک-اختیاری-به-هدر") expect(Slug.encoded_generator("آموزش اضافه کردن لینک اختیاری به هدر")).to eq("آموزش-اضافه-کردن-لینک-اختیاری-به-هدر")
expect(Slug.encoded_generator("熱帶風暴畫眉")).to eq("熱帶風暴畫眉") expect(Slug.encoded_generator("熱帶風暴畫眉")).to eq("熱帶風暴畫眉")
end end
it 'reject RFC 3986 reserved character and blank' do it 'reject RFC 3986 reserved character and blank' do
expect(Slug.encoded_generator(":/?#[]@!$ &'()*+,;=% -_`~.")).to eq("") expect(Slug.encoded_generator(":/?#[]@!$ &'()*+,;=% -_`~.")).to eq("---") # will be clear by #for
expect(Slug.encoded_generator(" - English and Chinese title with special characters / 中文标题 !@:?\\:'`#^& $%&*()` -- ")).to eq("English-and-Chinese-title-with-special-characters-中文标题")
end end
it 'generates null when nothing' do it 'generates null when nothing' do
@ -129,6 +156,10 @@ describe Slug do
it "keeps number unchanged" do it "keeps number unchanged" do
expect(Slug.encoded_generator('123')).to eq('123') expect(Slug.encoded_generator('123')).to eq('123')
end end
it 'downcase the string' do
expect(Slug.encoded_generator("LoWer")).to eq('lower')
end
end end
describe '#none_generator' do describe '#none_generator' do

View File

@ -338,7 +338,7 @@ describe CategoriesController do
expect(@category.reload.slug).to eq('valid-slug') expect(@category.reload.slug).to eq('valid-slug')
end end
it 'accepts and sanitize custom slug when the slug generation method is not english' do it 'accepts and sanitize custom slug when the slug generation method is not ascii' do
SiteSetting.slug_generation_method = 'none' SiteSetting.slug_generation_method = 'none'
put :update_slug, put :update_slug,
params: { category_id: @category.id, slug: ' another !_ slug @' }, params: { category_id: @category.id, slug: ' another !_ slug @' },