Skip to content

Commit

Permalink
@mention provides a context variable for overriding username pattern
Browse files Browse the repository at this point in the history
* This allows for easier customization of the usernam mention pattern regex
  • Loading branch information
Britt Ballard committed Oct 7, 2014
1 parent aeadfbd commit 57f6cce
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 19 deletions.
48 changes: 31 additions & 17 deletions lib/html/pipeline/@mention_filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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
Expand All @@ -79,18 +87,24 @@ 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.
#
# text - String text to replace @mention usernames in.
# 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)
Expand Down
28 changes: 26 additions & 2 deletions test/html/pipeline/mention_filter_test.rb
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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 = "<p>@_abc: test.</p>"
doc = Nokogiri::HTML::DocumentFragment.parse(body)

res = filter(doc, '/', nil, /(_[a-z]{3})/)

link = "<a href=\"/_abc\" class=\"user-mention\">@_abc</a>"
assert_equal "<p>#{link}: test.</p>",
res.to_html
end

def test_filter_does_not_create_a_new_object_for_default_username_pattern
body = "<div>@test</div>"
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

0 comments on commit 57f6cce

Please sign in to comment.