twitter-text 1.14.7 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rspec +1 -1
- data/README.md +104 -33
- data/lib/assets/tld_lib.yml +1 -0
- data/lib/twitter-text.rb +2 -0
- data/lib/twitter-text/autolink.rb +4 -4
- data/lib/twitter-text/configuration.rb +53 -0
- data/lib/twitter-text/deprecation.rb +1 -1
- data/lib/twitter-text/extractor.rb +31 -1
- data/lib/twitter-text/regex.rb +13 -13
- data/lib/twitter-text/validation.rb +155 -43
- data/lib/twitter-text/weighted_range.rb +18 -0
- data/spec/autolinking_spec.rb +161 -161
- data/spec/configuration_spec.rb +91 -0
- data/spec/extractor_spec.rb +92 -72
- data/spec/hithighlighter_spec.rb +15 -15
- data/spec/regex_spec.rb +7 -7
- data/spec/rewriter_spec.rb +110 -109
- data/spec/spec_helper.rb +13 -15
- data/spec/test_urls.rb +6 -4
- data/spec/twitter_text_spec.rb +2 -2
- data/spec/unicode_spec.rb +10 -10
- data/spec/validation_spec.rb +35 -11
- data/test/conformance_test.rb +14 -0
- data/twitter-text.gemspec +11 -9
- metadata +53 -32
- data/lib/assets/tld_lib.yml +0 -1565
data/spec/spec_helper.rb
CHANGED
@@ -34,7 +34,7 @@ RSpec::Matchers.define :match_autolink_expression_in do |text|
|
|
34
34
|
@match_data && @match_data.to_s.strip == url
|
35
35
|
end
|
36
36
|
|
37
|
-
|
37
|
+
failure_message do |url|
|
38
38
|
"Expected to find url '#{url}' in text '#{text}', but the match was #{@match_data.captures}'"
|
39
39
|
end
|
40
40
|
end
|
@@ -47,7 +47,7 @@ RSpec::Matchers.define :have_autolinked_url do |url, inner_text|
|
|
47
47
|
(inner_text && @link.inner_text == inner_text) || (!inner_text && @link.inner_text == url)
|
48
48
|
end
|
49
49
|
|
50
|
-
|
50
|
+
failure_message do |text|
|
51
51
|
"Expected url '#{url}'#{", inner_text '#{inner_text}'" if inner_text} to be autolinked in '#{text}'"
|
52
52
|
end
|
53
53
|
end
|
@@ -57,12 +57,11 @@ RSpec::Matchers.define :link_to_screen_name do |screen_name, inner_text|
|
|
57
57
|
|
58
58
|
match do |text|
|
59
59
|
@link = Nokogiri::HTML(text).search("a.username")
|
60
|
-
@link &&
|
61
|
-
@link.
|
62
|
-
"https://twitter.com/#{screen_name}".should == @link.first['href']
|
60
|
+
return false unless @link && @link.inner_text == expected
|
61
|
+
expect("https://twitter.com/#{screen_name}").to eq(@link.first['href'])
|
63
62
|
end
|
64
63
|
|
65
|
-
|
64
|
+
failure_message do |text|
|
66
65
|
if @link.first
|
67
66
|
"Expected link '#{@link.inner_text}' with href '#{@link.first['href']}' to match screen_name '#{expected}', but it does not."
|
68
67
|
else
|
@@ -70,7 +69,7 @@ RSpec::Matchers.define :link_to_screen_name do |screen_name, inner_text|
|
|
70
69
|
end
|
71
70
|
end
|
72
71
|
|
73
|
-
|
72
|
+
failure_message_when_negated do |text|
|
74
73
|
"Expected link '#{@link.inner_text}' with href '#{@link.first['href']}' not to match screen_name '#{expected}', but it does."
|
75
74
|
end
|
76
75
|
|
@@ -84,12 +83,11 @@ RSpec::Matchers.define :link_to_list_path do |list_path, inner_text|
|
|
84
83
|
|
85
84
|
match do |text|
|
86
85
|
@link = Nokogiri::HTML(text).search("a.list-slug")
|
87
|
-
@link &&
|
88
|
-
@link.
|
89
|
-
"https://twitter.com/#{list_path}".downcase.should == @link.first['href']
|
86
|
+
return false unless @link && @link.inner_text == expected
|
87
|
+
expect("https://twitter.com/#{list_path}".downcase).to eq(@link.first['href'])
|
90
88
|
end
|
91
89
|
|
92
|
-
|
90
|
+
failure_message do |text|
|
93
91
|
if @link.first
|
94
92
|
"Expected link '#{@link.inner_text}' with href '#{@link.first['href']}' to match the list path '#{expected}', but it does not."
|
95
93
|
else
|
@@ -97,7 +95,7 @@ RSpec::Matchers.define :link_to_list_path do |list_path, inner_text|
|
|
97
95
|
end
|
98
96
|
end
|
99
97
|
|
100
|
-
|
98
|
+
failure_message_when_negated do |text|
|
101
99
|
"Expected link '#{@link.inner_text}' with href '#{@link.first['href']}' not to match the list path '#{expected}', but it does."
|
102
100
|
end
|
103
101
|
|
@@ -108,13 +106,13 @@ end
|
|
108
106
|
|
109
107
|
RSpec::Matchers.define :have_autolinked_hashtag do |hashtag|
|
110
108
|
match do |text|
|
111
|
-
@link = Nokogiri::HTML(text).search("a[@href='https://twitter.com
|
109
|
+
@link = Nokogiri::HTML(text).search("a[@href='https://twitter.com/search?q=#{hashtag.sub(/^#/, '%23')}']")
|
112
110
|
@link &&
|
113
111
|
@link.inner_text &&
|
114
112
|
@link.inner_text == hashtag
|
115
113
|
end
|
116
114
|
|
117
|
-
|
115
|
+
failure_message do |text|
|
118
116
|
if @link.first
|
119
117
|
"Expected link text to be [#{hashtag}], but it was [#{@link.inner_text}] in #{text}"
|
120
118
|
else
|
@@ -122,7 +120,7 @@ RSpec::Matchers.define :have_autolinked_hashtag do |hashtag|
|
|
122
120
|
end
|
123
121
|
end
|
124
122
|
|
125
|
-
|
123
|
+
failure_message_when_negated do |text|
|
126
124
|
"Expected link '#{@link.inner_text}' with href '#{@link.first['href']}' not to match the hashtag '#{hashtag}', but it does."
|
127
125
|
end
|
128
126
|
end
|
data/spec/test_urls.rb
CHANGED
@@ -10,6 +10,8 @@ module TestUrls
|
|
10
10
|
"http://somedomain.com/index.php?path=/abc/def/",
|
11
11
|
"http://www.boingboing.net/2007/02/14/katamari_damacy_phon.html",
|
12
12
|
"http://somehost.com:3000",
|
13
|
+
"http://a_b.c-d.com",
|
14
|
+
"http://sub_domain-dash.twitter.com",
|
13
15
|
"http://xo.com/~matthew+%-x",
|
14
16
|
"http://en.wikipedia.org/wiki/Primer_(film)",
|
15
17
|
"http://www.ams.org/bookstore-getitem/item=mbk-59",
|
@@ -22,9 +24,7 @@ module TestUrls
|
|
22
24
|
"http://mrs.domain-dash.biz",
|
23
25
|
"http://x.com/has/one/char/domain",
|
24
26
|
"http://t.co/nwcLTFF",
|
25
|
-
"http://sub_domain-dash.twitter.com",
|
26
27
|
"http://a.b.cd",
|
27
|
-
"http://a_b.c-d.com",
|
28
28
|
"http://a-b.b.com",
|
29
29
|
"http://twitter-dash.com",
|
30
30
|
"http://msdn.microsoft.com/ja-jp/library/system.net.httpwebrequest(v=VS.100).aspx",
|
@@ -36,7 +36,8 @@ module TestUrls
|
|
36
36
|
"http://foobar.みんな",
|
37
37
|
"http://foobar.中国",
|
38
38
|
"http://foobar.پاکستان",
|
39
|
-
"https://www.youtube.com/playlist?list=PL0ZPu8XSRTB7wZzn0mLHMvyzVFeRxbWn-"
|
39
|
+
"https://www.youtube.com/playlist?list=PL0ZPu8XSRTB7wZzn0mLHMvyzVFeRxbWn-",
|
40
|
+
"http://ああ.com"
|
40
41
|
] unless defined?(TestUrls::VALID)
|
41
42
|
|
42
43
|
INVALID = [
|
@@ -56,7 +57,8 @@ module TestUrls
|
|
56
57
|
"http://twitt#{[0x202B].pack('U')}er.com",
|
57
58
|
"http://twitt#{[0x202C].pack('U')}er.com",
|
58
59
|
"http://twitt#{[0x202D].pack('U')}er.com",
|
59
|
-
"http://twitt#{[0x202E].pack('U')}er.com"
|
60
|
+
"http://twitt#{[0x202E].pack('U')}er.com",
|
61
|
+
"https://somesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurl.com/foo https://somesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurl.com/foo https://somesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurlsomesuperlongurl.com/foo"
|
60
62
|
] unless defined?(TestUrls::INVALID)
|
61
63
|
|
62
64
|
TCO = [
|
data/spec/twitter_text_spec.rb
CHANGED
data/spec/unicode_spec.rb
CHANGED
@@ -4,28 +4,28 @@ require File.dirname(__FILE__) + '/spec_helper'
|
|
4
4
|
describe Twitter::Unicode do
|
5
5
|
|
6
6
|
it "should lazy-init constants" do
|
7
|
-
Twitter::Unicode.const_defined?(:UFEB6).
|
8
|
-
Twitter::Unicode::UFEB6.
|
9
|
-
Twitter::Unicode::UFEB6.
|
10
|
-
Twitter::Unicode.const_defined?(:UFEB6).
|
7
|
+
expect(Twitter::Unicode.const_defined?(:UFEB6)).to eq(false)
|
8
|
+
expect(Twitter::Unicode::UFEB6).to_not be_nil
|
9
|
+
expect(Twitter::Unicode::UFEB6).to be_kind_of(String)
|
10
|
+
expect(Twitter::Unicode.const_defined?(:UFEB6)).to eq(true)
|
11
11
|
end
|
12
12
|
|
13
13
|
it "should return corresponding character" do
|
14
|
-
Twitter::Unicode::UFEB6.
|
14
|
+
expect(Twitter::Unicode::UFEB6).to be == [0xfeb6].pack('U')
|
15
15
|
end
|
16
16
|
|
17
17
|
it "should allow lowercase notation" do
|
18
|
-
Twitter::Unicode::Ufeb6.
|
19
|
-
Twitter::Unicode::Ufeb6.
|
18
|
+
expect(Twitter::Unicode::Ufeb6).to be == Twitter::Unicode::UFEB6
|
19
|
+
expect(Twitter::Unicode::Ufeb6).to be === Twitter::Unicode::UFEB6
|
20
20
|
end
|
21
21
|
|
22
22
|
it "should allow underscore notation" do
|
23
|
-
Twitter::Unicode::U_FEB6.
|
24
|
-
Twitter::Unicode::U_FEB6.
|
23
|
+
expect(Twitter::Unicode::U_FEB6).to be == Twitter::Unicode::UFEB6
|
24
|
+
expect(Twitter::Unicode::U_FEB6).to be === Twitter::Unicode::UFEB6
|
25
25
|
end
|
26
26
|
|
27
27
|
it "should raise on invalid codepoints" do
|
28
|
-
lambda { Twitter::Unicode::FFFFFF }.
|
28
|
+
expect(lambda { Twitter::Unicode::FFFFFF }).to raise_error(NameError)
|
29
29
|
end
|
30
30
|
|
31
31
|
end
|
data/spec/validation_spec.rb
CHANGED
@@ -8,36 +8,60 @@ end
|
|
8
8
|
describe Twitter::Validation do
|
9
9
|
|
10
10
|
it "should disallow invalid BOM character" do
|
11
|
-
TestValidation.new.tweet_invalid?("Bom:#{Twitter::Unicode::UFFFE}").
|
12
|
-
TestValidation.new.tweet_invalid?("Bom:#{Twitter::Unicode::UFEFF}").
|
11
|
+
expect(TestValidation.new.tweet_invalid?("Bom:#{Twitter::Unicode::UFFFE}")).to be == :invalid_characters
|
12
|
+
expect(TestValidation.new.tweet_invalid?("Bom:#{Twitter::Unicode::UFEFF}")).to be == :invalid_characters
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should disallow invalid U+FFFF character" do
|
16
|
-
TestValidation.new.tweet_invalid?("Bom:#{Twitter::Unicode::UFFFF}").
|
16
|
+
expect(TestValidation.new.tweet_invalid?("Bom:#{Twitter::Unicode::UFFFF}")).to be == :invalid_characters
|
17
17
|
end
|
18
18
|
|
19
19
|
it "should disallow direction change characters" do
|
20
20
|
[0x202A, 0x202B, 0x202C, 0x202D, 0x202E].map{|cp| [cp].pack('U') }.each do |char|
|
21
|
-
TestValidation.new.tweet_invalid?("Invalid:#{char}").
|
21
|
+
expect(TestValidation.new.tweet_invalid?("Invalid:#{char}")).to eq(:invalid_characters)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
25
|
it "should disallow non-Unicode" do
|
26
|
-
TestValidation.new.tweet_invalid?("not-Unicode:\xfff0").
|
26
|
+
expect(TestValidation.new.tweet_invalid?("not-Unicode:\xfff0")).to be == :invalid_characters
|
27
27
|
end
|
28
28
|
|
29
29
|
it "should allow <= 140 combined accent characters" do
|
30
30
|
char = [0x65, 0x0301].pack('U')
|
31
|
-
TestValidation.new.tweet_invalid?(char * 139).
|
32
|
-
TestValidation.new.tweet_invalid?(char * 140).
|
33
|
-
TestValidation.new.tweet_invalid?(char * 141).
|
31
|
+
expect(TestValidation.new.tweet_invalid?(char * 139)).to be false
|
32
|
+
expect(TestValidation.new.tweet_invalid?(char * 140)).to be false
|
33
|
+
expect(TestValidation.new.tweet_invalid?(char * 141)).to eq(:too_long)
|
34
34
|
end
|
35
35
|
|
36
36
|
it "should allow <= 140 multi-byte characters" do
|
37
37
|
char = [ 0x1d106 ].pack('U')
|
38
|
-
TestValidation.new.tweet_invalid?(char * 139).
|
39
|
-
TestValidation.new.tweet_invalid?(char * 140).
|
40
|
-
TestValidation.new.tweet_invalid?(char * 141).
|
38
|
+
expect(TestValidation.new.tweet_invalid?(char * 139)).to be false
|
39
|
+
expect(TestValidation.new.tweet_invalid?(char * 140)).to be false
|
40
|
+
expect(TestValidation.new.tweet_invalid?(char * 141)).to eq(:too_long)
|
41
41
|
end
|
42
42
|
|
43
|
+
context "when returning results" do
|
44
|
+
it "should properly create new fully-populated results from arguments" do
|
45
|
+
results = Twitter::Validation::ParseResults.new(weighted_length: 26, permillage: 92, valid: true, display_range_start: 0, display_range_end: 16, valid_range_start: 0, valid_range_end:16)
|
46
|
+
expect(results).to_not be nil
|
47
|
+
expect(results[:weighted_length]).to eq(26)
|
48
|
+
expect(results[:permillage]).to eq(92)
|
49
|
+
expect(results[:valid]).to be true
|
50
|
+
expect(results[:display_range_start]).to eq(0)
|
51
|
+
expect(results[:display_range_end]).to eq(16)
|
52
|
+
expect(results[:valid_range_start]).to eq(0)
|
53
|
+
expect(results[:valid_range_end]).to eq(16)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should properly create empty results" do
|
57
|
+
results = Twitter::Validation::ParseResults.empty()
|
58
|
+
expect(results[:weighted_length]).to eq(0)
|
59
|
+
expect(results[:permillage]).to eq(0)
|
60
|
+
expect(results[:valid]).to be true
|
61
|
+
expect(results[:display_range_start]).to eq(0)
|
62
|
+
expect(results[:display_range_end]).to eq(0)
|
63
|
+
expect(results[:valid_range_start]).to eq(0)
|
64
|
+
expect(results[:valid_range_end]).to eq(0)
|
65
|
+
end
|
66
|
+
end
|
43
67
|
end
|
data/test/conformance_test.rb
CHANGED
@@ -62,6 +62,16 @@ class ConformanceTest < Test::Unit::TestCase
|
|
62
62
|
element.attribute_nodes.map{|attr| [attr.name, attr.value]}.sort
|
63
63
|
end
|
64
64
|
|
65
|
+
def assert_equal_parse_results(expected, actual, failure_message = nil)
|
66
|
+
e = {}
|
67
|
+
# Note that we don't assert display and valid ranges because of differences
|
68
|
+
# in how ruby counts characters (wrt surrogate pairs) vs. other platforms
|
69
|
+
range_keys = [:display_range_start, :display_range_end, :valid_range_start, :valid_range_end]
|
70
|
+
expected.keys.each { |k| e[k.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase.to_sym] = expected[k] }
|
71
|
+
[e, actual].each { |a| range_keys.each { |k| a.delete(k) } }
|
72
|
+
assert_equal e, actual, failure_message
|
73
|
+
end
|
74
|
+
|
65
75
|
CONFORMANCE_DIR = ENV['CONFORMANCE_DIR'] || File.expand_path("../../../conformance", __FILE__)
|
66
76
|
|
67
77
|
def self.def_conformance_test(file, test_type, &block)
|
@@ -208,4 +218,8 @@ class ConformanceTest < Test::Unit::TestCase
|
|
208
218
|
def_conformance_test("validate.yml", :lengths) do
|
209
219
|
assert_equal expected, tweet_length(text), description
|
210
220
|
end
|
221
|
+
|
222
|
+
def_conformance_test("validate.yml", :WeightedTweetsCounterTest) do
|
223
|
+
assert_equal_parse_results expected, parse_tweet(text), description
|
224
|
+
end
|
211
225
|
end
|
data/twitter-text.gemspec
CHANGED
@@ -2,11 +2,10 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "twitter-text"
|
5
|
-
s.version = "
|
6
|
-
s.authors = ["
|
7
|
-
"
|
8
|
-
s.email = ["
|
9
|
-
"raffi@twitter.com", "jcummins@twitter.com", "niw@niw.at", "keita@twitter.com", "jkoval@twitter.com"]
|
5
|
+
s.version = "2.0.0"
|
6
|
+
s.authors = ["David LaMacchia", "Sudheer Guntupalli", "Kaushik Lakshmikanth", "Jose Antonio Marquez Russo", "Lee Adams",
|
7
|
+
"Yoshimasa Niwa"]
|
8
|
+
s.email = ["opensource@twitter.com"]
|
10
9
|
s.homepage = "http://twitter.com"
|
11
10
|
s.description = s.summary = "A gem that provides text handling for Twitter"
|
12
11
|
s.license = "Apache 2.0"
|
@@ -15,14 +14,17 @@ Gem::Specification.new do |s|
|
|
15
14
|
s.has_rdoc = true
|
16
15
|
s.summary = "Twitter text handling library"
|
17
16
|
|
17
|
+
s.add_development_dependency "pry"
|
18
18
|
s.add_development_dependency "test-unit"
|
19
19
|
s.add_development_dependency "multi_json", "~> 1.3"
|
20
|
-
s.add_development_dependency "nokogiri", "~> 1.
|
21
|
-
s.add_development_dependency "rake"
|
20
|
+
s.add_development_dependency "nokogiri", "~> 1.8.0"
|
21
|
+
s.add_development_dependency "rake"
|
22
22
|
s.add_development_dependency "rdoc"
|
23
|
-
s.add_development_dependency "rspec", "~>
|
24
|
-
s.add_development_dependency "simplecov"
|
23
|
+
s.add_development_dependency "rspec", "~> 3.0"
|
24
|
+
s.add_development_dependency "simplecov"
|
25
25
|
s.add_runtime_dependency "unf", "~> 0.1.0"
|
26
|
+
# Use of idn-ruby requires libidn to be installed separately
|
27
|
+
s.add_runtime_dependency "idn-ruby"
|
26
28
|
|
27
29
|
s.files = `git ls-files`.split("\n") + ['lib/assets/tld_lib.yml']
|
28
30
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
metadata
CHANGED
@@ -1,23 +1,34 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: twitter-text
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
8
|
-
-
|
9
|
-
-
|
10
|
-
-
|
11
|
-
-
|
12
|
-
- J.P. Cummins
|
7
|
+
- David LaMacchia
|
8
|
+
- Sudheer Guntupalli
|
9
|
+
- Kaushik Lakshmikanth
|
10
|
+
- Jose Antonio Marquez Russo
|
11
|
+
- Lee Adams
|
13
12
|
- Yoshimasa Niwa
|
14
|
-
- Keita Fujii
|
15
|
-
- James Koval
|
16
13
|
autorequire:
|
17
14
|
bindir: bin
|
18
15
|
cert_chain: []
|
19
|
-
date: 2017-
|
16
|
+
date: 2017-12-15 00:00:00.000000000 Z
|
20
17
|
dependencies:
|
18
|
+
- !ruby/object:Gem::Dependency
|
19
|
+
name: pry
|
20
|
+
requirement: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '0'
|
25
|
+
type: :development
|
26
|
+
prerelease: false
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '0'
|
21
32
|
- !ruby/object:Gem::Dependency
|
22
33
|
name: test-unit
|
23
34
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,28 +63,28 @@ dependencies:
|
|
52
63
|
requirements:
|
53
64
|
- - "~>"
|
54
65
|
- !ruby/object:Gem::Version
|
55
|
-
version: 1.
|
66
|
+
version: 1.8.0
|
56
67
|
type: :development
|
57
68
|
prerelease: false
|
58
69
|
version_requirements: !ruby/object:Gem::Requirement
|
59
70
|
requirements:
|
60
71
|
- - "~>"
|
61
72
|
- !ruby/object:Gem::Version
|
62
|
-
version: 1.
|
73
|
+
version: 1.8.0
|
63
74
|
- !ruby/object:Gem::Dependency
|
64
75
|
name: rake
|
65
76
|
requirement: !ruby/object:Gem::Requirement
|
66
77
|
requirements:
|
67
|
-
- - "
|
78
|
+
- - ">="
|
68
79
|
- !ruby/object:Gem::Version
|
69
|
-
version: '
|
80
|
+
version: '0'
|
70
81
|
type: :development
|
71
82
|
prerelease: false
|
72
83
|
version_requirements: !ruby/object:Gem::Requirement
|
73
84
|
requirements:
|
74
|
-
- - "
|
85
|
+
- - ">="
|
75
86
|
- !ruby/object:Gem::Version
|
76
|
-
version: '
|
87
|
+
version: '0'
|
77
88
|
- !ruby/object:Gem::Dependency
|
78
89
|
name: rdoc
|
79
90
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,28 +105,28 @@ dependencies:
|
|
94
105
|
requirements:
|
95
106
|
- - "~>"
|
96
107
|
- !ruby/object:Gem::Version
|
97
|
-
version:
|
108
|
+
version: '3.0'
|
98
109
|
type: :development
|
99
110
|
prerelease: false
|
100
111
|
version_requirements: !ruby/object:Gem::Requirement
|
101
112
|
requirements:
|
102
113
|
- - "~>"
|
103
114
|
- !ruby/object:Gem::Version
|
104
|
-
version:
|
115
|
+
version: '3.0'
|
105
116
|
- !ruby/object:Gem::Dependency
|
106
117
|
name: simplecov
|
107
118
|
requirement: !ruby/object:Gem::Requirement
|
108
119
|
requirements:
|
109
|
-
- - "
|
120
|
+
- - ">="
|
110
121
|
- !ruby/object:Gem::Version
|
111
|
-
version: 0
|
122
|
+
version: '0'
|
112
123
|
type: :development
|
113
124
|
prerelease: false
|
114
125
|
version_requirements: !ruby/object:Gem::Requirement
|
115
126
|
requirements:
|
116
|
-
- - "
|
127
|
+
- - ">="
|
117
128
|
- !ruby/object:Gem::Version
|
118
|
-
version: 0
|
129
|
+
version: '0'
|
119
130
|
- !ruby/object:Gem::Dependency
|
120
131
|
name: unf
|
121
132
|
requirement: !ruby/object:Gem::Requirement
|
@@ -130,17 +141,23 @@ dependencies:
|
|
130
141
|
- - "~>"
|
131
142
|
- !ruby/object:Gem::Version
|
132
143
|
version: 0.1.0
|
144
|
+
- !ruby/object:Gem::Dependency
|
145
|
+
name: idn-ruby
|
146
|
+
requirement: !ruby/object:Gem::Requirement
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '0'
|
151
|
+
type: :runtime
|
152
|
+
prerelease: false
|
153
|
+
version_requirements: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
133
158
|
description: A gem that provides text handling for Twitter
|
134
159
|
email:
|
135
|
-
-
|
136
|
-
- patrick.henry.ewing@gmail.com
|
137
|
-
- bcherry@gmail.com
|
138
|
-
- bs@brittspace.com
|
139
|
-
- raffi@twitter.com
|
140
|
-
- jcummins@twitter.com
|
141
|
-
- niw@niw.at
|
142
|
-
- keita@twitter.com
|
143
|
-
- jkoval@twitter.com
|
160
|
+
- opensource@twitter.com
|
144
161
|
executables: []
|
145
162
|
extensions: []
|
146
163
|
extra_rdoc_files: []
|
@@ -156,6 +173,7 @@ files:
|
|
156
173
|
- lib/assets/tld_lib.yml
|
157
174
|
- lib/twitter-text.rb
|
158
175
|
- lib/twitter-text/autolink.rb
|
176
|
+
- lib/twitter-text/configuration.rb
|
159
177
|
- lib/twitter-text/deprecation.rb
|
160
178
|
- lib/twitter-text/extractor.rb
|
161
179
|
- lib/twitter-text/hash_helper.rb
|
@@ -164,9 +182,11 @@ files:
|
|
164
182
|
- lib/twitter-text/rewriter.rb
|
165
183
|
- lib/twitter-text/unicode.rb
|
166
184
|
- lib/twitter-text/validation.rb
|
185
|
+
- lib/twitter-text/weighted_range.rb
|
167
186
|
- script/destroy
|
168
187
|
- script/generate
|
169
188
|
- spec/autolinking_spec.rb
|
189
|
+
- spec/configuration_spec.rb
|
170
190
|
- spec/extractor_spec.rb
|
171
191
|
- spec/hithighlighter_spec.rb
|
172
192
|
- spec/regex_spec.rb
|
@@ -198,12 +218,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
198
218
|
version: '0'
|
199
219
|
requirements: []
|
200
220
|
rubyforge_project:
|
201
|
-
rubygems_version: 2.
|
221
|
+
rubygems_version: 2.7.0
|
202
222
|
signing_key:
|
203
223
|
specification_version: 4
|
204
224
|
summary: Twitter text handling library
|
205
225
|
test_files:
|
206
226
|
- spec/autolinking_spec.rb
|
227
|
+
- spec/configuration_spec.rb
|
207
228
|
- spec/extractor_spec.rb
|
208
229
|
- spec/hithighlighter_spec.rb
|
209
230
|
- spec/regex_spec.rb
|