diff --git a/lib/diaspora/taggable.rb b/lib/diaspora/taggable.rb
index 87ca36417..388fc56d7 100644
--- a/lib/diaspora/taggable.rb
+++ b/lib/diaspora/taggable.rb
@@ -4,8 +4,6 @@
module Diaspora
module Taggable
- VALID_TAG_BODY = /[^_,\s#*\[\]()\@\/"'\.%]+\b/
-
def self.included(model)
model.class_eval do
cattr_accessor :field_with_tags
@@ -43,10 +41,16 @@ module Diaspora
return text if opts[:plain_text]
text = ERB::Util.h(text) unless opts[:no_escape]
- regex = /(^|\s|>)#(#{VALID_TAG_BODY})/
+ regex = /(^|\s|>)#([\w-]+|<3)/
text.to_str.gsub(regex) { |matched_string|
- %{#{$1}##{$2}}
+ pre, url_bit, clickable = $1, $2, "##{$2}"
+ if $2 == '<3'
+ # Special case for love, because the world needs more love.
+ url_bit = '<3'
+ end
+
+ %{#{pre}#{clickable}}
}.html_safe
end
end
diff --git a/spec/shared_behaviors/taggable.rb b/spec/shared_behaviors/taggable.rb
index c84b7d629..cec1e60fa 100644
--- a/spec/shared_behaviors/taggable.rb
+++ b/spec/shared_behaviors/taggable.rb
@@ -12,6 +12,10 @@ describe Diaspora::Taggable do
def controller
end
+ def tag_link(s)
+ link_to "##{s}", "/tags/#{s}", :class => 'tag'
+ end
+
describe '.format_tags' do
before do
@str = '#what #hey #vöglein'
@@ -21,13 +25,59 @@ describe Diaspora::Taggable do
end
it 'links the tag to /p' do
- link = link_to('#vöglein', '/tags/vöglein', :class => 'tag')
- Diaspora::Taggable.format_tags(@str).should include(link)
+ Diaspora::Taggable.format_tags(@str).should include( tag_link('vöglein') )
end
it 'responds to plain_text' do
Diaspora::Taggable.format_tags(@str, :plain_text => true).should == @str
end
+
+ it "doesn't mangle text when tags are involved" do
+ expected = {
+ nil => '',
+ '' => '',
+ 'abc' => 'abc',
+ 'a #b c' => "a #{tag_link('b')} c",
+ '#' => '#',
+ '##' => '##',
+ '###' => '###',
+ '#a' => tag_link('a'),
+ '#foobar' => tag_link('foobar'),
+ '#foocar
' => "#{tag_link('foocar')}<br>",
+ '#fooo@oo' => "#{tag_link('fooo')}@oo",
+ '#num3ric hash tags' => "#{tag_link('num3ric')} hash tags",
+ '#12345 tag' => "#{tag_link('12345')} tag",
+ '#12cde tag' => "#{tag_link('12cde')} tag",
+ '#abc45 tag' => "#{tag_link('abc45')} tag",
+ '#<3' => %{#<3},
+ 'i #<3' => %{i #<3},
+ 'i #<3 you' => %{i #<3 you},
+ '#<4' => '#<4',
+ 'test#foo test' => 'test#foo test',
+ 'test.#joo bar' => 'test.#joo bar',
+ 'test #foodar test' => "test #{tag_link('foodar')} test",
+ 'test #foofar
test' => "test #{tag_link('foofar')}<br> test",
+ 'test #gooo@oo test' => "test #{tag_link('gooo')}@oo test",
+ 'test #foo-test test' => "test #{tag_link('foo-test')} test",
+ 'test #hoo' => "test #{tag_link('hoo')}",
+ 'test #two_word tags' => "test #{tag_link('two_word')} tags",
+ 'test #three_word_tags' => "test #{tag_link('three_word_tags')}",
+ '#terminal_underscore_' => tag_link('terminal_underscore_'),
+ '#terminalunderscore_' => tag_link('terminalunderscore_'),
+ '#_initialunderscore' => tag_link('_initialunderscore'),
+ '#_initial_underscore' => tag_link('_initial_underscore'),
+ '#terminalhyphen-' => tag_link('terminalhyphen-'),
+ '#terminal-hyphen-' => tag_link('terminal-hyphen-'),
+ '#terminalhyphen- tag' => "#{tag_link('terminalhyphen-')} tag",
+ '#-initialhyphen' => tag_link('-initialhyphen'),
+ '#-initialhyphen tag' => "#{tag_link('-initialhyphen')} tag",
+ '#-initial-hyphen' => tag_link('-initial-hyphen'),
+ }
+
+ expected.each do |input,output|
+ Diaspora::Taggable.format_tags(input).should == output
+ end
+ end
end
describe '#build_tags' do
@@ -52,6 +102,11 @@ describe Diaspora::Taggable do
it 'extracts tags despite surrounding text' do
expected = {
+ '' => nil,
+ '#' => nil,
+ '##' => nil,
+ '###' => nil,
+ '#a' => 'a',
'#foobar' => 'foobar',
'#foocar
' => 'foocar',
'#fooo@oo' => 'fooo',
@@ -64,7 +119,7 @@ describe Diaspora::Taggable do
'test#foo test' => nil,
'test.#joo bar' => nil,
'test #foodar test' => 'foodar',
- 'test #foofar
test ' => 'foofar',
+ 'test #foofar
test' => 'foofar',
'test #gooo@oo test' => 'gooo',
'test #<3 test' => '<3',
'test #foo-test test' => 'foo-test',