linkup 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -0
- data/README.markdown +44 -0
- data/Rakefile +14 -0
- data/VERSION +1 -0
- data/doc/ruby_regex.rb +19 -0
- data/doc/sample_lines.txt +26 -0
- data/lib/linkup.rb +41 -0
- data/linkup.gemspec +49 -0
- data/test/test_helper.rb +7 -0
- data/test/test_linkup.rb +185 -0
- metadata +65 -0
data/.gitignore
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
linkup
|
2
|
+
===
|
3
|
+
|
4
|
+
A Ruby Gem. It will add link tags to url-like string embedded in text, without re-linking existing links.
|
5
|
+
|
6
|
+
Caveat: If the text contains already linked urls, then the original string will be returned. It's not smart about telling which urls are already linked or not linked, and so just returns the original string if it sees any href='s in the passed string.
|
7
|
+
|
8
|
+
Background
|
9
|
+
---
|
10
|
+
|
11
|
+
Based on this regex by John Gruber: http://daringfireball.net/2009/11/liberal_regex_for_matching_urls
|
12
|
+
|
13
|
+
Provide hyperlinks for plain text urls.
|
14
|
+
|
15
|
+
From the link:
|
16
|
+
|
17
|
+
"It makes no attempt to parse URLs according to any official specification. It isn’t limited to predefined URL protocols. It should be clever about things like parentheses and trailing punctuation."
|
18
|
+
|
19
|
+
Usage
|
20
|
+
---
|
21
|
+
|
22
|
+
The sane, non-object-space-polluting way:
|
23
|
+
|
24
|
+
Linkup.linkify('string with url http://example.com')
|
25
|
+
#=> 'string with url <a href="http://example.com">http://example.com</a>'
|
26
|
+
|
27
|
+
Or, include it in the String class To add a method called 'linkup' to every string object:
|
28
|
+
|
29
|
+
class String; include Linkup; end
|
30
|
+
|
31
|
+
'string with url http://example.com'.linkup
|
32
|
+
#=> 'string with url <a href="http://example.com">http://example.com</a>'
|
33
|
+
|
34
|
+
As per the warning above, strings already containing href='s will not be re-linked:
|
35
|
+
|
36
|
+
s = '<a href="http://example.com">http://foo.com</a>, http://bar.net/'
|
37
|
+
Linkup.linkify(s)
|
38
|
+
#=> '<a href="http://example.com">http://foo.com</a>, http://bar.net/'
|
39
|
+
|
40
|
+
Installation
|
41
|
+
---
|
42
|
+
|
43
|
+
gem install linkup
|
44
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |gemspec|
|
4
|
+
gemspec.name = "linkup"
|
5
|
+
gemspec.summary = "Link bare urls in text."
|
6
|
+
gemspec.description = "From: http://daringfireball.net/2009/11/liberal_regex_for_matching_urls"
|
7
|
+
gemspec.email = "jim@jimlindley.com"
|
8
|
+
gemspec.homepage = "http://github.com/jlindley/linkup"
|
9
|
+
gemspec.authors = ["Jim Lindley"]
|
10
|
+
end
|
11
|
+
Jeweler::GemcutterTasks.new
|
12
|
+
rescue LoadError
|
13
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
14
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
data/doc/ruby_regex.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
r = /\b(([\w-]+:\/\/?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|\/)))/
|
4
|
+
|
5
|
+
s1 = "Blah blah http://blah.com blah http://foo.com/alsdkjf.html"
|
6
|
+
s2 = "blah blah blah blah"
|
7
|
+
|
8
|
+
m1 = s1.scan(r)
|
9
|
+
puts m1.map{|m| m[0] }.join(" ")
|
10
|
+
|
11
|
+
m2 = s2.scan(r)
|
12
|
+
|
13
|
+
puts m2.map{|m| m[0] }.join(" ")
|
14
|
+
|
15
|
+
s1_linked = s1.gsub(r) do |url|
|
16
|
+
%Q[<a href="#{url}">#{url}</a>]
|
17
|
+
end
|
18
|
+
|
19
|
+
puts s1_linked
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# from http://daringfireball.net/2009/11/liberal_regex_for_matching_urls
|
2
|
+
|
3
|
+
http://foo.com/blah_blah
|
4
|
+
http://foo.com/blah_blah/
|
5
|
+
(Something like http://foo.com/blah_blah)
|
6
|
+
http://foo.com/blah_blah_(wikipedia)
|
7
|
+
(Something like http://foo.com/blah_blah_(wikipedia))
|
8
|
+
http://foo.com/blah_blah.
|
9
|
+
http://foo.com/blah_blah/.
|
10
|
+
<http://foo.com/blah_blah>
|
11
|
+
<http://foo.com/blah_blah/>
|
12
|
+
http://foo.com/blah_blah,
|
13
|
+
http://www.example.com/wpstyle/?p=364.
|
14
|
+
http://✪df.ws/123
|
15
|
+
rdar://1234
|
16
|
+
rdar:/1234
|
17
|
+
http://userid:password@example.com:8080
|
18
|
+
http://userid@example.com
|
19
|
+
http://userid@example.com:8080
|
20
|
+
http://userid:password@example.com
|
21
|
+
http://example.com:8080 x-yojimbo-item://6303E4C1-xxxx-45A6-AB9D-3A908F59AE0E
|
22
|
+
message://%3c330e7f8409726r6a4ba78dkf1fd71420c1bf6ff@mail.gmail.com%3e
|
23
|
+
http://➡.ws/䨹
|
24
|
+
www.➡.ws/䨹
|
25
|
+
<tag>http://example.com</tag>
|
26
|
+
Just a www.example.com link.
|
data/lib/linkup.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module Linkup
|
2
|
+
|
3
|
+
# for inclusion in String class
|
4
|
+
def linkup
|
5
|
+
Linkup.linkify(self)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.linkify(string)
|
9
|
+
Linker.new(string).to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
class Linker
|
13
|
+
|
14
|
+
attr_accessor :original
|
15
|
+
|
16
|
+
LINK_REGEX = /\b(([\w-]+:\/\/?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|\/)))/u
|
17
|
+
|
18
|
+
def initialize(string)
|
19
|
+
self.original = string
|
20
|
+
end
|
21
|
+
|
22
|
+
def already_linked?
|
23
|
+
original.match(/href=/)
|
24
|
+
end
|
25
|
+
|
26
|
+
def linkify_text(text)
|
27
|
+
text.gsub(LINK_REGEX) do |match|
|
28
|
+
prefix = if (match =~/\Awww\./)
|
29
|
+
prefix = 'http://'
|
30
|
+
end
|
31
|
+
%Q[<a href="#{prefix}#{match}">#{match}</a>]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
already_linked? ? original : linkify_text(original)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
data/linkup.gemspec
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{linkup}
|
8
|
+
s.version = "1.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Jim Lindley"]
|
12
|
+
s.date = %q{2010-01-20}
|
13
|
+
s.description = %q{From: http://daringfireball.net/2009/11/liberal_regex_for_matching_urls}
|
14
|
+
s.email = %q{jim@jimlindley.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.markdown"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".gitignore",
|
20
|
+
"README.markdown",
|
21
|
+
"Rakefile",
|
22
|
+
"VERSION",
|
23
|
+
"doc/ruby_regex.rb",
|
24
|
+
"doc/sample_lines.txt",
|
25
|
+
"lib/linkup.rb",
|
26
|
+
"linkup.gemspec",
|
27
|
+
"test/test_helper.rb",
|
28
|
+
"test/test_linkup.rb"
|
29
|
+
]
|
30
|
+
s.homepage = %q{http://github.com/jlindley/linkup}
|
31
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
32
|
+
s.require_paths = ["lib"]
|
33
|
+
s.rubygems_version = %q{1.3.5}
|
34
|
+
s.summary = %q{Link bare urls in text.}
|
35
|
+
s.test_files = [
|
36
|
+
"test/test_helper.rb",
|
37
|
+
"test/test_linkup.rb"
|
38
|
+
]
|
39
|
+
|
40
|
+
if s.respond_to? :specification_version then
|
41
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
42
|
+
s.specification_version = 3
|
43
|
+
|
44
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
45
|
+
else
|
46
|
+
end
|
47
|
+
else
|
48
|
+
end
|
49
|
+
end
|
data/test/test_helper.rb
ADDED
data/test/test_linkup.rb
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
class TestLinkup < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "from daringfireball" do
|
6
|
+
|
7
|
+
should "link plain url" do
|
8
|
+
input = 'http://foo.com/blah_blah'
|
9
|
+
expected = '<a href="http://foo.com/blah_blah">http://foo.com/blah_blah</a>'
|
10
|
+
assert_equal expected, input.linkup
|
11
|
+
end
|
12
|
+
|
13
|
+
should "link plain url with trailing slash" do
|
14
|
+
input = 'http://foo.com/blah_blah/'
|
15
|
+
expected = '<a href="http://foo.com/blah_blah/">http://foo.com/blah_blah/</a>'
|
16
|
+
assert_equal expected, input.linkup
|
17
|
+
end
|
18
|
+
|
19
|
+
should "not link dot after url with" do
|
20
|
+
input = 'http://foo.com/blah_blah.'
|
21
|
+
expected = '<a href="http://foo.com/blah_blah">http://foo.com/blah_blah</a>.'
|
22
|
+
assert_equal expected, input.linkup
|
23
|
+
end
|
24
|
+
|
25
|
+
should "link plain url with trailing slash, but not trailing dot" do
|
26
|
+
input = 'http://foo.com/blah_blah/.'
|
27
|
+
expected = '<a href="http://foo.com/blah_blah/">http://foo.com/blah_blah/</a>.'
|
28
|
+
assert_equal expected, input.linkup
|
29
|
+
end
|
30
|
+
|
31
|
+
should "not link link-adjacent parens" do
|
32
|
+
input = '(Something like http://foo.com/blah_blah)'
|
33
|
+
expected = '(Something like <a href="http://foo.com/blah_blah">http://foo.com/blah_blah</a>)'
|
34
|
+
assert_equal expected, input.linkup
|
35
|
+
end
|
36
|
+
|
37
|
+
should "link paren-containing link" do
|
38
|
+
input = 'http://foo.com/blah_blah_(wikipedia)'
|
39
|
+
expected = '<a href="http://foo.com/blah_blah_(wikipedia)">http://foo.com/blah_blah_(wikipedia)</a>'
|
40
|
+
assert_equal expected, input.linkup
|
41
|
+
end
|
42
|
+
|
43
|
+
should "handle mixed link and non-link parens" do
|
44
|
+
input = '(Something like http://foo.com/blah_blah_(wikipedia))'
|
45
|
+
expected = '(Something like <a href="http://foo.com/blah_blah_(wikipedia)">http://foo.com/blah_blah_(wikipedia)</a>)'
|
46
|
+
assert_equal expected, input.linkup
|
47
|
+
end
|
48
|
+
|
49
|
+
should "not link link-adjacent angle brackets" do
|
50
|
+
input = '<http://foo.com/blah_blah>'
|
51
|
+
expected = '<<a href="http://foo.com/blah_blah">http://foo.com/blah_blah</a>>'
|
52
|
+
assert_equal expected, input.linkup
|
53
|
+
end
|
54
|
+
|
55
|
+
should "handle link-adjacent angle brackets with trailing slash" do
|
56
|
+
input = '<http://foo.com/blah_blah/>'
|
57
|
+
expected = '<<a href="http://foo.com/blah_blah/">http://foo.com/blah_blah/</a>>'
|
58
|
+
assert_equal expected, input.linkup
|
59
|
+
end
|
60
|
+
|
61
|
+
should "not link comma after url" do
|
62
|
+
input = 'http://foo.com/blah_blah,'
|
63
|
+
expected = '<a href="http://foo.com/blah_blah">http://foo.com/blah_blah</a>,'
|
64
|
+
assert_equal expected, input.linkup
|
65
|
+
end
|
66
|
+
|
67
|
+
should "not link urls with params" do
|
68
|
+
input = 'http://www.example.com/wpstyle/?p=364.'
|
69
|
+
expected = '<a href="http://www.example.com/wpstyle/?p=364">http://www.example.com/wpstyle/?p=364</a>.'
|
70
|
+
assert_equal expected, input.linkup
|
71
|
+
end
|
72
|
+
|
73
|
+
should "link non-protocol specifying, but www-subdomained urls" do
|
74
|
+
input = "www.example.com"
|
75
|
+
expected = '<a href="http://www.example.com">www.example.com</a>'
|
76
|
+
assert_equal expected, input.linkup
|
77
|
+
end
|
78
|
+
|
79
|
+
should "link utf-8 IDN domains" do
|
80
|
+
input = 'http://✪df.ws/123'
|
81
|
+
expected = '<a href="http://✪df.ws/123">http://✪df.ws/123</a>'
|
82
|
+
assert_equal expected, input.linkup
|
83
|
+
end
|
84
|
+
|
85
|
+
should "link non-standard non-x-prefixed protocols" do
|
86
|
+
input = 'rdar://1234'
|
87
|
+
expected = '<a href="rdar://1234">rdar://1234</a>'
|
88
|
+
assert_equal expected, input.linkup
|
89
|
+
end
|
90
|
+
|
91
|
+
should "link link urls-like strings with only one slash" do
|
92
|
+
input = 'rdar:/1234'
|
93
|
+
expected = '<a href="rdar:/1234">rdar:/1234</a>'
|
94
|
+
assert_equal expected, input.linkup
|
95
|
+
end
|
96
|
+
|
97
|
+
should "link urls with port" do
|
98
|
+
input = 'http://example.com:8080'
|
99
|
+
expected = '<a href="http://example.com:8080">http://example.com:8080</a>'
|
100
|
+
assert_equal expected, input.linkup
|
101
|
+
end
|
102
|
+
|
103
|
+
should "link urls with username" do
|
104
|
+
input = 'http://userid@example.com'
|
105
|
+
expected = '<a href="http://userid@example.com">http://userid@example.com</a>'
|
106
|
+
assert_equal expected, input.linkup
|
107
|
+
end
|
108
|
+
|
109
|
+
should "link urls with username and password" do
|
110
|
+
input = 'http://userid:password@example.com'
|
111
|
+
expected = '<a href="http://userid:password@example.com">http://userid:password@example.com</a>'
|
112
|
+
assert_equal expected, input.linkup
|
113
|
+
end
|
114
|
+
|
115
|
+
should "link urls with username and port" do
|
116
|
+
input = 'http://userid@example.com:8080'
|
117
|
+
expected = '<a href="http://userid@example.com:8080">http://userid@example.com:8080</a>'
|
118
|
+
assert_equal expected, input.linkup
|
119
|
+
end
|
120
|
+
|
121
|
+
should "link urls with username and password and port" do
|
122
|
+
input = 'http://userid:password@example.com:8080'
|
123
|
+
expected = '<a href="http://userid:password@example.com:8080">http://userid:password@example.com:8080</a>'
|
124
|
+
assert_equal expected, input.linkup
|
125
|
+
end
|
126
|
+
|
127
|
+
should "link urls with custom x- prefixed protocols" do
|
128
|
+
input = 'x-yojimbo-item://6303E4C1-xxxx-45A6-AB9D-3A908F59AE0E'
|
129
|
+
expected = '<a href="x-yojimbo-item://6303E4C1-xxxx-45A6-AB9D-3A908F59AE0E">x-yojimbo-item://6303E4C1-xxxx-45A6-AB9D-3A908F59AE0E</a>'
|
130
|
+
assert_equal expected, input.linkup
|
131
|
+
end
|
132
|
+
|
133
|
+
should "link mail message urls" do
|
134
|
+
input = 'message://%3c330e7f8409726r6a4ba78dkf1fd71420c1bf6ff@mail.gmail.com%3e'
|
135
|
+
expected = '<a href="message://%3c330e7f8409726r6a4ba78dkf1fd71420c1bf6ff@mail.gmail.com%3e">message://%3c330e7f8409726r6a4ba78dkf1fd71420c1bf6ff@mail.gmail.com%3e</a>'
|
136
|
+
assert_equal expected, input.linkup
|
137
|
+
end
|
138
|
+
|
139
|
+
should "link urls with UTF-8 domain and path components" do
|
140
|
+
input = "http://➡.ws/䨹"
|
141
|
+
expected = '<a href="http://➡.ws/䨹">http://➡.ws/䨹</a>'
|
142
|
+
assert_equal expected, input.linkup
|
143
|
+
end
|
144
|
+
|
145
|
+
should "link urls with UTF-8 domain and path components, www subdomain, but no protocol" do
|
146
|
+
input = "www.➡.ws/䨹"
|
147
|
+
expected = '<a href="http://www.➡.ws/䨹">www.➡.ws/䨹</a>'
|
148
|
+
assert_equal expected, input.linkup
|
149
|
+
end
|
150
|
+
|
151
|
+
should "link urls inside existing xml tags" do
|
152
|
+
input = "<tag>http://example.com</tag>"
|
153
|
+
expected = '<tag><a href="http://example.com">http://example.com</a></tag>'
|
154
|
+
assert_equal expected, input.linkup
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
context "already-linked urls" do
|
160
|
+
should "not re-link urls inside link tags" do
|
161
|
+
input = '<a href="http://example.com">http://example.com</a>'
|
162
|
+
expected = '<a href="http://example.com">http://example.com</a>'
|
163
|
+
assert_equal expected, input.linkup
|
164
|
+
end
|
165
|
+
should "not re-link urls inside link tags with surrounding text" do
|
166
|
+
input = '<a href="http://example.com">click http://example.com to go there</a>'
|
167
|
+
expected = '<a href="http://example.com">click http://example.com to go there</a>'
|
168
|
+
assert_equal expected, input.linkup
|
169
|
+
end
|
170
|
+
should "not re-link urls inside link tags, even with intervening tags" do
|
171
|
+
input = '<a href="http://example.com"><i>http://example.com</i></a>'
|
172
|
+
expected = '<a href="http://example.com"><i>http://example.com</i></a>'
|
173
|
+
assert_equal expected, input.linkup
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context "badly handled scenarios" do
|
178
|
+
should "but doesn't, link non-linked urls next to linked urls" do
|
179
|
+
input = '<a href="http://example.com"><i>http://example.com</i></a>, and also http://example.net/ too'
|
180
|
+
expected = '<a href="http://example.com"><i>http://example.com</i></a>, and also http://example.net/ too'
|
181
|
+
assert_equal expected, input.linkup
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: linkup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jim Lindley
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-01-20 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: "From: http://daringfireball.net/2009/11/liberal_regex_for_matching_urls"
|
17
|
+
email: jim@jimlindley.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.markdown
|
24
|
+
files:
|
25
|
+
- .gitignore
|
26
|
+
- README.markdown
|
27
|
+
- Rakefile
|
28
|
+
- VERSION
|
29
|
+
- doc/ruby_regex.rb
|
30
|
+
- doc/sample_lines.txt
|
31
|
+
- lib/linkup.rb
|
32
|
+
- linkup.gemspec
|
33
|
+
- test/test_helper.rb
|
34
|
+
- test/test_linkup.rb
|
35
|
+
has_rdoc: true
|
36
|
+
homepage: http://github.com/jlindley/linkup
|
37
|
+
licenses: []
|
38
|
+
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options:
|
41
|
+
- --charset=UTF-8
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: "0"
|
49
|
+
version:
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
requirements: []
|
57
|
+
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 1.3.5
|
60
|
+
signing_key:
|
61
|
+
specification_version: 3
|
62
|
+
summary: Link bare urls in text.
|
63
|
+
test_files:
|
64
|
+
- test/test_helper.rb
|
65
|
+
- test/test_linkup.rb
|