diff --git a/app/lib/permalink_normalizer.rb b/app/lib/permalink_normalizer.rb new file mode 100644 index 0000000..dd4e1f5 --- /dev/null +++ b/app/lib/permalink_normalizer.rb @@ -0,0 +1,16 @@ +class PermalinkNormalizer + attr_accessor :text, :max_length + + def initialize(text, max_length: 50) + self.text = text + self.max_length = max_length + end + + def normalize + text + .gsub(/((?!\p{Alnum}|\p{So}).)+/, "-") + .delete_prefix("-") + .delete_suffix("-") + .downcase[0..max_length - 1] + end +end diff --git a/app/models/concerns/permalinkable.rb b/app/models/concerns/permalinkable.rb index 9210ada..eebe55a 100644 --- a/app/models/concerns/permalinkable.rb +++ b/app/models/concerns/permalinkable.rb @@ -10,7 +10,7 @@ module Permalinkable def generate_permalink(count = 0) column_value = attributes.fetch(self.class.permalink_column) - new_permalink = column_value.parameterize[0..49] + new_permalink = PermalinkNormalizer.new(column_value).normalize if count > 0 new_permalink += "-#{count}" diff --git a/spec/lib/permalink_normalizer_spec.rb b/spec/lib/permalink_normalizer_spec.rb new file mode 100644 index 0000000..eb23d64 --- /dev/null +++ b/spec/lib/permalink_normalizer_spec.rb @@ -0,0 +1,48 @@ +# coding: utf-8 +require 'rails_helper' + +RSpec.describe PermalinkNormalizer do + describe "#normalize" do + subject { PermalinkNormalizer.new(text, max_length: 50).normalize } + + let(:text) { "Hello world!" } + + it { is_expected.to eql("hello-world") } + + context "with a very long title" do + let(:text) { "x" * 200 } + + it { is_expected.to eql("x" * 50) } + end + + context "with bad characters at start and end" do + let(:text) { "~ hello world ~" } + + it { is_expected.to eql("hello-world") } + end + + context "with numbers" do + let(:text) { "3 days ago" } + + it { is_expected.to eql("3-days-ago") } + end + + context "with diacritics" do + let(:text) { "naïve café" } + + it { is_expected.to eql("naïve-café") } + end + + context "with kanji and kana" do + let(:text) { "初めまして、ダニエルです。" } + + it { is_expected.to eql("初めまして-ダニエルです") } + end + + context "with emoji" do + let(:text) { "💎 is a gemstone 😃" } + + it { is_expected.to eql("💎-is-a-gemstone-😃") } + end + end +end diff --git a/spec/models/article_spec.rb b/spec/models/article_spec.rb index 05d3928..4e9477d 100644 --- a/spec/models/article_spec.rb +++ b/spec/models/article_spec.rb @@ -9,16 +9,6 @@ FactoryBot.build(:article, title: "Hello world!", category: category) } - it { is_expected.to eql("hello-world") } - - context "with a very long title" do - let(:article) { - FactoryBot.build_stubbed(:article, title: "x" * 200) - } - - it { is_expected.to eql("x" * 50) } - end - context "with existing article having the same title" do before do FactoryBot.create(:article, title: "Hello world!", category: category)