From 57f6cce37cd2e7f941a3b8371b9f6e7597512347 Mon Sep 17 00:00:00 2001 From: Britt Ballard Date: Thu, 2 Oct 2014 17:53:14 -0500 Subject: [PATCH] @mention provides a context variable for overriding username pattern * This allows for easier customization of the usernam mention pattern regex --- lib/html/pipeline/@mention_filter.rb | 48 +++++++++++++++-------- test/html/pipeline/mention_filter_test.rb | 28 ++++++++++++- 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/lib/html/pipeline/@mention_filter.rb b/lib/html/pipeline/@mention_filter.rb index 03af2430..5ec1530e 100644 --- a/lib/html/pipeline/@mention_filter.rb +++ b/lib/html/pipeline/@mention_filter.rb @@ -11,6 +11,8 @@ class Pipeline # mention. # :info_url - Used to link to "more info" when someone mentions @mention # or @mentioned. + # :username_pattern - Used to provide a custom regular expression to + # identify usernames # class MentionFilter < Filter # Public: Find user @mentions in text. See @@ -27,25 +29,31 @@ class MentionFilter < Filter # the original text. # # Returns a String replaced with the return of the block. - def self.mentioned_logins_in(text) - text.gsub MentionPattern do |match| + def self.mentioned_logins_in(text, username_pattern=UsernamePattern) + text.gsub MentionPatterns[username_pattern] do |match| login = $1 yield match, login, MentionLogins.include?(login.downcase) end end - # Pattern used to extract @mentions from text. - MentionPattern = / - (?:^|\W) # beginning of string or non-word char - @((?>[a-z0-9][a-z0-9-]*)) # @username - (?!\/) # without a trailing slash - (?= - \.+[ \t\W]| # dots followed by space or non-word character - \.+$| # dots at end of line - [^0-9a-zA-Z_.]| # non-word character except dot - $ # end of line - ) - /ix + # Hash that contains all of the mention patterns used by the pipeline + MentionPatterns = Hash.new do |hash, key| + hash[key] = / + (?:^|\W) # beginning of string or non-word char + @((?>#{key})) # @username + (?!\/) # without a trailing slash + (?= + \.+[ \t\W]| # dots followed by space or non-word character + \.+$| # dots at end of line + [^0-9a-zA-Z_.]| # non-word character except dot + $ # end of line + ) + /ix + end + + # Default pattern used to extract usernames from text. The value can be + # overriden by providing the username_pattern variable in the context. + UsernamePattern = /[a-z0-9][a-z0-9-]*/ # List of username logins that, when mentioned, link to the blog post # about @mentions instead of triggering a real mention. @@ -66,7 +74,7 @@ def call content = node.to_html next if !content.include?('@') next if has_ancestor?(node, IGNORE_PARENTS) - html = mention_link_filter(content, base_url, info_url) + html = mention_link_filter(content, base_url, info_url, username_pattern) next if html == content node.replace(html) end @@ -79,6 +87,10 @@ def info_url context[:info_url] || nil end + def username_pattern + context[:username_pattern] || UsernamePattern + end + # Replace user @mentions in text with links to the mentioned user's # profile page. # @@ -86,11 +98,13 @@ def info_url # base_url - The base URL used to construct user profile URLs. # info_url - The "more info" URL used to link to more info on @mentions. # If nil we don't link @mention or @mentioned. + # username_pattern - Regular expression used to identify usernames in + # text # # Returns a string with @mentions replaced with links. All links have a # 'user-mention' class name attached for styling. - def mention_link_filter(text, base_url='/', info_url=nil) - self.class.mentioned_logins_in(text) do |match, login, is_mentioned| + def mention_link_filter(text, base_url='/', info_url=nil, username_pattern) + self.class.mentioned_logins_in(text, username_pattern) do |match, login, is_mentioned| link = if is_mentioned link_to_mention_info(login, info_url) diff --git a/test/html/pipeline/mention_filter_test.rb b/test/html/pipeline/mention_filter_test.rb index cce1d6cb..54c7f0bc 100644 --- a/test/html/pipeline/mention_filter_test.rb +++ b/test/html/pipeline/mention_filter_test.rb @@ -1,8 +1,8 @@ require "test_helper" class HTML::Pipeline::MentionFilterTest < Minitest::Test - def filter(html, base_url='/', info_url=nil) - HTML::Pipeline::MentionFilter.call(html, :base_url => base_url, :info_url => info_url) + def filter(html, base_url='/', info_url=nil, username_pattern=nil) + HTML::Pipeline::MentionFilter.call(html, :base_url => base_url, :info_url => info_url, :username_pattern => username_pattern) end def test_filtering_a_documentfragment @@ -158,4 +158,28 @@ def test_mention_at_end_of_parenthetical_sentence @body = "(We're talking 'bout @ymendel.)" assert_equal %w[ymendel], mentioned_usernames end + + def test_username_pattern_can_be_customized + body = "

@_abc: test.

" + doc = Nokogiri::HTML::DocumentFragment.parse(body) + + res = filter(doc, '/', nil, /(_[a-z]{3})/) + + link = "@_abc" + assert_equal "

#{link}: test.

", + res.to_html + end + + def test_filter_does_not_create_a_new_object_for_default_username_pattern + body = "
@test
" + doc = Nokogiri::HTML::DocumentFragment.parse(body) + + filter(doc.clone, '/', nil) + pattern_count = HTML::Pipeline::MentionFilter::MentionPatterns.length + filter(doc.clone, '/', nil) + + assert_equal pattern_count, HTML::Pipeline::MentionFilter::MentionPatterns.length + filter(doc.clone, '/', nil, /test/) + assert_equal pattern_count + 1, HTML::Pipeline::MentionFilter::MentionPatterns.length + end end