From f1fd108a01e46c679d7c0e07605e6659dc9c657c Mon Sep 17 00:00:00 2001 From: Tom Hughes Date: Mon, 8 Dec 2025 12:41:45 +0000 Subject: [PATCH] Simplify linkify normalisation to use a single array of rules --- config/settings.yml | 17 +++++++---------- lib/rich_text.rb | 23 ++++++++++++----------- test/lib/rich_text_test.rb | 27 +++++++++++---------------- 3 files changed, 30 insertions(+), 37 deletions(-) diff --git a/config/settings.yml b/config/settings.yml index 8ff54049a..21e9693ac 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -139,6 +139,7 @@ fossgis_valhalla_url: "https://valhalla1.openstreetmap.de/route" # Endpoints for Wikimedia integration wikidata_api_url: "https://www.wikidata.org/w/api.php" wikimedia_commons_url: "https://commons.wikimedia.org/wiki/" +# Linkification rules linkify: detection_rules: - patterns: @@ -186,16 +187,12 @@ linkify: replacement: "\\k=*" - pattern: "osm\\.wiki/Tag:(?[^\"?#<>/\\s]+)(?:=|%3D)(?[^\"?#<>\\s]+)" replacement: "\\k=\\k" -# Main website hosts to match in linkify -linkify_hosts: ["www.openstreetmap.org", "www.osm.org", "www.openstreetmap.com", "openstreetmap.org", "osm.org", "openstreetmap.com"] -# Shorter host to replace main hosts -linkify_hosts_replacement: "osm.org" -# Wiki website hosts to match in linkify -linkify_wiki_hosts: ["wiki.openstreetmap.org", "wiki.osm.org", "wiki.openstreetmap.com", "wiki.openstreetmaps.org", "osm.wiki", "www.osm.wiki", "wiki.osm.wiki"] -# Shorter host to replace wiki hosts -linkify_wiki_hosts_replacement: "osm.wiki" -# Regexp for wiki prefix that can be removed -linkify_wiki_optional_path_prefix: "^/wiki(?=/[A-Z])" + normalisation_rules: + - hosts: ["www.openstreetmap.org", "www.osm.org", "www.openstreetmap.com", "openstreetmap.org", "osm.org", "openstreetmap.com"] + host_replacement: "osm.org" + - hosts : ["wiki.openstreetmap.org", "wiki.osm.org", "wiki.openstreetmap.com", "wiki.openstreetmaps.org", "osm.wiki", "www.osm.wiki", "wiki.osm.wiki"] + host_replacement: "osm.wiki" + optional_path_prefix: "^/wiki(?=/[A-Z])" # External authentication credentials #google_auth_id: "" #google_auth_secret: "" diff --git a/lib/rich_text.rb b/lib/rich_text.rb index e22de9333..76ba29254 100644 --- a/lib/rich_text.rb +++ b/lib/rich_text.rb @@ -107,14 +107,12 @@ module RichText end def expand_host_shorthands(text) - [ - [Settings.linkify_hosts, Settings.linkify_hosts_replacement], - [Settings.linkify_wiki_hosts, Settings.linkify_wiki_hosts_replacement] - ] - .select { |hosts, replacement| replacement && hosts&.any? } - .reduce(text) do |text, (hosts, replacement)| - text.gsub(/(?<=^|#{URL_UNSAFE_CHARS})\b#{Regexp.escape(replacement)}/) do - "#{Settings.server_protocol}://#{hosts[0]}" + Array + .wrap(Settings.linkify&.normalisation_rules) + .select { |rule| rule.host_replacement && rule.hosts&.any? } + .reduce(text) do |text, rule| + text.gsub(/(?<=^|#{URL_UNSAFE_CHARS})\b#{Regexp.escape(rule.host_replacement)}/) do + "#{Settings.server_protocol}://#{rule.hosts[0]}" end end end @@ -125,9 +123,12 @@ module RichText end def format_link_text(url) - url = shorten_host(url, Settings.linkify_hosts, Settings.linkify_hosts_replacement) - url = shorten_host(url, Settings.linkify_wiki_hosts, Settings.linkify_wiki_hosts_replacement) do |path| - path.sub(Regexp.new(Settings.linkify_wiki_optional_path_prefix || ""), "") + url = Array + .wrap(Settings.linkify&.normalisation_rules) + .reduce(url) do |normalised_url, rule| + shorten_host(normalised_url, rule.hosts, rule.host_replacement) do |path| + path.sub(Regexp.new(rule.optional_path_prefix || ""), "") + end end Array.wrap(Settings.linkify&.display_rules) .select { |rule| rule.pattern && rule.replacement } diff --git a/test/lib/rich_text_test.rb b/test/lib/rich_text_test.rb index af9abd8a9..58c0db882 100644 --- a/test/lib/rich_text_test.rb +++ b/test/lib/rich_text_test.rb @@ -214,7 +214,7 @@ class RichTextTest < ActiveSupport::TestCase end def test_text_to_html_linkify - with_settings(:linkify_hosts => ["replace-me.example.com"], :linkify_hosts_replacement => "repl.example.com") do + with_settings(:linkify => { :normalisation_rules => [{ :hosts => ["replace-me.example.com"], :host_replacement => "repl.example.com" }] }) do r = RichText.new("text", "foo http://example.com/ bar") assert_html r do assert_dom "a", :count => 1, :text => "http://example.com/" do @@ -226,7 +226,7 @@ class RichTextTest < ActiveSupport::TestCase end def test_text_to_html_linkify_replace - with_settings(:linkify_hosts => ["replace-me.example.com"], :linkify_hosts_replacement => "repl.example.com") do + with_settings(:linkify => { :normalisation_rules => [{ :hosts => ["replace-me.example.com"], :host_replacement => "repl.example.com" }] }) do r = RichText.new("text", "foo https://replace-me.example.com/some/path?query=te10#result12 bar") assert_html r do assert_dom "a", :count => 1, :text => "repl.example.com/some/path?query=te10#result12" do @@ -238,7 +238,7 @@ class RichTextTest < ActiveSupport::TestCase end def test_text_to_html_linkify_recognize - with_settings(:linkify_hosts => ["replace-me.example.com"], :linkify_hosts_replacement => "repl.example.com") do + with_settings(:linkify => { :normalisation_rules => [{ :hosts => ["replace-me.example.com"], :host_replacement => "repl.example.com" }] }) do r = RichText.new("text", "foo repl.example.com/some/path?query=te10#result12 bar") assert_html r do assert_dom "a", :count => 1, :text => "repl.example.com/some/path?query=te10#result12" do @@ -250,7 +250,7 @@ class RichTextTest < ActiveSupport::TestCase end def test_text_to_html_linkify_replace_other_scheme - with_settings(:linkify_hosts => ["replace-me.example.com"], :linkify_hosts_replacement => "repl.example.com") do + with_settings(:linkify => { :normalisation_rules => [{ :hosts => ["replace-me.example.com"], :host_replacement => "repl.example.com" }] }) do r = RichText.new("text", "foo ftp://replace-me.example.com/some/path?query=te10#result12 bar") assert_html r do assert_dom "a", :count => 1, :text => "ftp://replace-me.example.com/some/path?query=te10#result12" do @@ -262,7 +262,7 @@ class RichTextTest < ActiveSupport::TestCase end def test_text_to_html_linkify_replace_undefined - with_settings(:linkify_hosts => ["replace-me.example.com"], :linkify_hosts_replacement => nil) do + with_settings(:linkify => { :normalisation_rules => [{ :hosts => ["replace-me.example.com"] }] }) do r = RichText.new("text", "foo https://replace-me.example.com/some/path?query=te10#result12 bar") assert_html r do assert_dom "a", :count => 1, :text => "https://replace-me.example.com/some/path?query=te10#result12" do @@ -274,8 +274,7 @@ class RichTextTest < ActiveSupport::TestCase end def test_text_to_html_linkify_wiki_replace_prefix - with_settings(:linkify_wiki_hosts => ["replace-me-wiki.example.com"], :linkify_wiki_hosts_replacement => "wiki.example.com", - :linkify_wiki_optional_path_prefix => "^/wiki(?=/[A-Z])") do + with_settings(:linkify => { :normalisation_rules => [{ :hosts => ["replace-me-wiki.example.com"], :host_replacement => "wiki.example.com", :optional_path_prefix => "^/wiki(?=/[A-Z])" }] }) do r = RichText.new("text", "foo https://replace-me-wiki.example.com/wiki/Tag:surface%3Dmetal bar") assert_html r do assert_dom "a", :count => 1, :text => "wiki.example.com/Tag:surface%3Dmetal" do @@ -287,8 +286,7 @@ class RichTextTest < ActiveSupport::TestCase end def test_text_to_html_linkify_wiki_replace_prefix_undefined - with_settings(:linkify_wiki_hosts => ["replace-me-wiki.example.com"], :linkify_wiki_hosts_replacement => "wiki.example.com", - :linkify_wiki_optional_path_prefix => nil) do + with_settings(:linkify => { :normalisation_rules => [{ :hosts => ["replace-me-wiki.example.com"], :host_replacement => "wiki.example.com" }] }) do r = RichText.new("text", "foo https://replace-me-wiki.example.com/wiki/Tag:surface%3Dmetal bar") assert_html r do assert_dom "a", :count => 1, :text => "wiki.example.com/wiki/Tag:surface%3Dmetal" do @@ -300,8 +298,7 @@ class RichTextTest < ActiveSupport::TestCase end def test_text_to_html_linkify_wiki_replace_undefined_prefix - with_settings(:linkify_wiki_hosts => ["replace-me-wiki.example.com"], :linkify_wiki_hosts_replacement => nil, - :linkify_wiki_optional_path_prefix => "^/wiki(?=/[A-Z])") do + with_settings(:linkify => { :normalisation_rules => [{ :hosts => ["replace-me-wiki.example.com"], :optional_path_prefix => "^/wiki(?=/[A-Z])" }] }) do r = RichText.new("text", "foo https://replace-me-wiki.example.com/wiki/Tag:surface%3Dmetal bar") assert_html r do assert_dom "a", :count => 1, :text => "https://replace-me-wiki.example.com/Tag:surface%3Dmetal" do @@ -313,8 +310,7 @@ class RichTextTest < ActiveSupport::TestCase end def test_text_to_html_linkify_wiki_replace_prefix_no_match - with_settings(:linkify_wiki_hosts => ["replace-me-wiki.example.com"], :linkify_wiki_hosts_replacement => "wiki.example.com", - :linkify_wiki_optional_path_prefix => "^/wiki(?=/[A-Z])") do + with_settings(:linkify => { :normalisation_rules => [{ :hosts => ["replace-me-wiki.example.com"], :host_replacement => "wiki.example.com", :optional_path_prefix => "^/wiki(?=/[A-Z])" }] }) do r = RichText.new("text", "foo https://replace-me-wiki.example.com/wiki/w bar") assert_html r do assert_dom "a", :count => 1, :text => "wiki.example.com/wiki/w" do @@ -326,8 +322,7 @@ class RichTextTest < ActiveSupport::TestCase end def test_text_to_html_linkify_recognize_wiki - with_settings(:linkify_wiki_hosts => ["replace-me-wiki.example.com"], :linkify_wiki_hosts_replacement => "wiki.example.com", - :linkify_wiki_optional_path_prefix => "^/wiki(?=/[A-Z])") do + with_settings(:linkify => { :normalisation_rules => [{ :hosts => ["replace-me-wiki.example.com"], :host_replacement => "wiki.example.com", :optional_path_prefix => "^/wiki(?=/[A-Z])" }] }) do r = RichText.new("text", "foo wiki.example.com/Tag:surface%3Dmetal bar") assert_html r do assert_dom "a", :count => 1, :text => "wiki.example.com/Tag:surface%3Dmetal" do @@ -339,7 +334,7 @@ class RichTextTest < ActiveSupport::TestCase end def test_text_to_html_linkify_idempotent - with_settings(:linkify_hosts => ["test.host"], :linkify_hosts_replacement => "test.host") do + with_settings(:linkify => { :normalisation_rules => [{ :hosts => ["test.host"], :host_replacement => "test.host" }] }) do t0 = "foo https://test.host/way/123456789 bar" r1 = RichText.new("text", t0) -- 2.39.5