rinku 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/COPYING +13 -0
- data/README.markdown +87 -0
- data/Rakefile +71 -0
- data/VERSION +1 -0
- data/ext/rinku/autolink.c +239 -0
- data/ext/rinku/autolink.h +39 -0
- data/ext/rinku/buffer.c +323 -0
- data/ext/rinku/buffer.h +154 -0
- data/ext/rinku/extconf.rb +4 -0
- data/ext/rinku/html_autolink.c +221 -0
- data/ext/rinku/rinku.c +86 -0
- data/lib/rinku.rb +41 -0
- data/rinku.gemspec +34 -0
- data/test/autolink_test.rb +135 -0
- metadata +80 -0
data/ext/rinku/rinku.c
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include "ruby.h"
|
3
|
+
|
4
|
+
#include "autolink.h"
|
5
|
+
#include "buffer.h"
|
6
|
+
|
7
|
+
static VALUE rb_cRinku;
|
8
|
+
|
9
|
+
extern void
|
10
|
+
upshtml_autolink(
|
11
|
+
struct buf *ob,
|
12
|
+
struct buf *text,
|
13
|
+
unsigned int flags,
|
14
|
+
const char *link_attr,
|
15
|
+
void (*link_text_cb)(struct buf *ob, const struct buf *link, void *payload),
|
16
|
+
void *payload);
|
17
|
+
|
18
|
+
static void
|
19
|
+
autolink_callback(struct buf *link_text, const struct buf *link, void *block)
|
20
|
+
{
|
21
|
+
VALUE rb_link, rb_link_text;
|
22
|
+
rb_link = rb_str_new(link->data, link->size);
|
23
|
+
rb_link_text = rb_funcall((VALUE)block, rb_intern("call"), 1, rb_link);
|
24
|
+
Check_Type(rb_link_text, T_STRING);
|
25
|
+
bufput(link_text, RSTRING_PTR(rb_link_text), RSTRING_LEN(rb_link_text));
|
26
|
+
}
|
27
|
+
|
28
|
+
static VALUE
|
29
|
+
rb_rinku_autolink(int argc, VALUE *argv, VALUE self)
|
30
|
+
{
|
31
|
+
VALUE result, rb_text, rb_mode, rb_html, rb_block;
|
32
|
+
struct buf input_buf = {0, 0, 0, 0, 0}, *output_buf;
|
33
|
+
int link_mode;
|
34
|
+
const char *link_attr = NULL;
|
35
|
+
ID mode_sym;
|
36
|
+
|
37
|
+
rb_scan_args(argc, argv, "3&", &rb_text, &rb_mode, &rb_html, &rb_block);
|
38
|
+
|
39
|
+
Check_Type(rb_text, T_STRING);
|
40
|
+
Check_Type(rb_mode, T_SYMBOL);
|
41
|
+
|
42
|
+
if (!NIL_P(rb_html)) {
|
43
|
+
Check_Type(rb_html, T_STRING);
|
44
|
+
link_attr = RSTRING_PTR(rb_html);
|
45
|
+
}
|
46
|
+
|
47
|
+
input_buf.data = RSTRING_PTR(rb_text);
|
48
|
+
input_buf.size = RSTRING_LEN(rb_text);
|
49
|
+
output_buf = bufnew(128);
|
50
|
+
|
51
|
+
mode_sym = SYM2ID(rb_mode);
|
52
|
+
|
53
|
+
if (mode_sym == rb_intern("all"))
|
54
|
+
link_mode = AUTOLINK_ALL;
|
55
|
+
else if (mode_sym == rb_intern("email_addresses"))
|
56
|
+
link_mode = AUTOLINK_EMAILS;
|
57
|
+
else if (mode_sym == rb_intern("urls"))
|
58
|
+
link_mode = AUTOLINK_URLS;
|
59
|
+
else
|
60
|
+
rb_raise(rb_eTypeError,
|
61
|
+
"Invalid linking mode (possible values are :all, :urls, :email_addresses)");
|
62
|
+
|
63
|
+
if (RTEST(rb_block))
|
64
|
+
upshtml_autolink(output_buf, &input_buf, AUTOLINK_ALL, link_attr, &autolink_callback, (void*)rb_block);
|
65
|
+
else
|
66
|
+
upshtml_autolink(output_buf, &input_buf, AUTOLINK_ALL, link_attr, NULL, NULL);
|
67
|
+
|
68
|
+
result = rb_str_new(output_buf->data, output_buf->size);
|
69
|
+
bufrelease(output_buf);
|
70
|
+
|
71
|
+
/* force the input encoding */
|
72
|
+
if (rb_respond_to(rb_text, rb_intern("encoding"))) {
|
73
|
+
VALUE encoding = rb_funcall(rb_text, rb_intern("encoding"), 0);
|
74
|
+
rb_funcall(result, rb_intern("force_encoding"), 1, encoding);
|
75
|
+
}
|
76
|
+
|
77
|
+
return result;
|
78
|
+
}
|
79
|
+
|
80
|
+
void Init_rinku()
|
81
|
+
{
|
82
|
+
rb_cRinku = rb_define_class("Rinku", rb_cObject);
|
83
|
+
rb_define_singleton_method(rb_cRinku, "_auto_link", rb_rinku_autolink, -1);
|
84
|
+
}
|
85
|
+
|
86
|
+
|
data/lib/rinku.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'cgi'
|
3
|
+
|
4
|
+
class Rinku
|
5
|
+
def self.auto_link(text, *args, &block)
|
6
|
+
return '' if text.strip.empty?
|
7
|
+
|
8
|
+
options = args.size == 2 ? {} : (args.last.is_a?(::Hash) ? args.pop : {})
|
9
|
+
unless args.empty?
|
10
|
+
options[:link] = args[0] || :all
|
11
|
+
options[:html] = args[1] || {}
|
12
|
+
end
|
13
|
+
|
14
|
+
_auto_link(text, options[:link] || :all, tag_options(options[:html] || {}), &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer
|
19
|
+
autoplay controls loop selected hidden scoped async
|
20
|
+
defer reversed ismap seemless muted required
|
21
|
+
autofocus novalidate formnovalidate open).to_set
|
22
|
+
|
23
|
+
def self.tag_options(options, escape = true)
|
24
|
+
unless options.empty?
|
25
|
+
attrs = []
|
26
|
+
options.each_pair do |key, value|
|
27
|
+
key = key.to_s
|
28
|
+
if BOOLEAN_ATTRIBUTES.include?(key)
|
29
|
+
attrs << %(#{key}="#{key}") if value
|
30
|
+
elsif !value.nil?
|
31
|
+
final_value = value.is_a?(Array) ? value.join(" ") : value
|
32
|
+
final_value = CGI.escapeHTML(final_value) if escape
|
33
|
+
attrs << %(#{key}="#{final_value}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
" #{attrs.sort * ' '}" unless attrs.empty?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
require 'rinku.so'
|
data/rinku.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'rinku'
|
3
|
+
s.version = File.open('VERSION').read.strip
|
4
|
+
s.summary = "Mostly autolinking"
|
5
|
+
s.description = <<-EOF
|
6
|
+
A fast and very smart autolinking library that
|
7
|
+
acts as a drop-in replacement for Rails `auto_link`
|
8
|
+
EOF
|
9
|
+
s.email = 'vicent@github.com'
|
10
|
+
s.homepage = 'http://github.com/tanoku/rinku'
|
11
|
+
s.authors = ["Vicent Martí"]
|
12
|
+
# = MANIFEST =
|
13
|
+
s.files = %w[
|
14
|
+
COPYING
|
15
|
+
VERSION
|
16
|
+
README.markdown
|
17
|
+
Rakefile
|
18
|
+
ext/rinku/rinku.c
|
19
|
+
ext/rinku/autolink.c
|
20
|
+
ext/rinku/autolink.h
|
21
|
+
ext/rinku/buffer.c
|
22
|
+
ext/rinku/buffer.h
|
23
|
+
ext/rinku/html_autolink.c
|
24
|
+
ext/rinku/extconf.rb
|
25
|
+
lib/rinku.rb
|
26
|
+
rinku.gemspec
|
27
|
+
test/autolink_test.rb
|
28
|
+
]
|
29
|
+
# = MANIFEST =
|
30
|
+
s.test_files = ["test/autolink_test.rb"]
|
31
|
+
s.extra_rdoc_files = ["COPYING"]
|
32
|
+
s.extensions = ["ext/rinku/extconf.rb"]
|
33
|
+
s.require_paths = ["lib"]
|
34
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
rootdir = File.dirname(File.dirname(__FILE__))
|
3
|
+
$LOAD_PATH.unshift "#{rootdir}/lib"
|
4
|
+
|
5
|
+
require 'test/unit'
|
6
|
+
require 'cgi'
|
7
|
+
require 'rinku'
|
8
|
+
|
9
|
+
class RedcarpetAutolinkTest < Test::Unit::TestCase
|
10
|
+
def assert_linked(expected, url)
|
11
|
+
assert_equal expected, Rinku.auto_link(url)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_block
|
15
|
+
link = Rinku.auto_link("Find ur favorite pokeman @ http://www.pokemon.com") do |url|
|
16
|
+
assert_equal url, "http://www.pokemon.com"
|
17
|
+
"POKEMAN WEBSITE"
|
18
|
+
end
|
19
|
+
|
20
|
+
assert_equal link, "Find ur favorite pokeman @ <a href=\"http://www.pokemon.com\">POKEMAN WEBSITE</a>"
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_link_attributes
|
24
|
+
assert_equal Rinku.auto_link("http://www.bash.org", :html => {:target => "_blank"}),
|
25
|
+
"<a target=\"_blank\" href=\"http://www.bash.org\">http://www.bash.org</a>"
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_autolink_works
|
29
|
+
url = "http://example.com/"
|
30
|
+
assert_linked "<a href=\"#{url}\">#{url}</a>", url
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_not_autolink_www
|
34
|
+
assert_linked "Awww... man", "Awww... man"
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_does_not_terminate_on_dash
|
38
|
+
url = "http://example.com/Notification_Center-GitHub-20101108-140050.jpg"
|
39
|
+
assert_linked "<a href=\"#{url}\">#{url}</a>", url
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_does_not_include_trailing_gt
|
43
|
+
url = "http://example.com"
|
44
|
+
assert_linked "<<a href=\"#{url}\">#{url}</a>>", "<#{url}>"
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_links_with_anchors
|
48
|
+
url = "https://github.com/github/hubot/blob/master/scripts/cream.js#L20-20"
|
49
|
+
assert_linked "<a href=\"#{url}\">#{url}</a>", url
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_links_like_rails
|
53
|
+
urls = %w(http://www.rubyonrails.com
|
54
|
+
http://www.rubyonrails.com:80
|
55
|
+
http://www.rubyonrails.com/~minam
|
56
|
+
https://www.rubyonrails.com/~minam
|
57
|
+
http://www.rubyonrails.com/~minam/url%20with%20spaces
|
58
|
+
http://www.rubyonrails.com/foo.cgi?something=here
|
59
|
+
http://www.rubyonrails.com/foo.cgi?something=here&and=here
|
60
|
+
http://www.rubyonrails.com/contact;new
|
61
|
+
http://www.rubyonrails.com/contact;new%20with%20spaces
|
62
|
+
http://www.rubyonrails.com/contact;new?with=query&string=params
|
63
|
+
http://www.rubyonrails.com/~minam/contact;new?with=query&string=params
|
64
|
+
http://en.wikipedia.org/wiki/Wikipedia:Today%27s_featured_picture_%28animation%29/January_20%2C_2007
|
65
|
+
http://www.mail-archive.com/rails@lists.rubyonrails.org/
|
66
|
+
http://www.amazon.com/Testing-Equal-Sign-In-Path/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1198861734&sr=8-1
|
67
|
+
http://en.wikipedia.org/wiki/Sprite_(computer_graphics)
|
68
|
+
http://en.wikipedia.org/wiki/Texas_hold'em
|
69
|
+
https://www.google.com/doku.php?id=gps:resource:scs:start
|
70
|
+
)
|
71
|
+
|
72
|
+
urls.each do |url|
|
73
|
+
assert_linked %(<a href="#{url}">#{CGI.escapeHTML(url)}</a>), url
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_links_like_autolink_rails
|
78
|
+
email_raw = 'david@loudthinking.com'
|
79
|
+
email_result = %{<a href="mailto:#{email_raw}">#{email_raw}</a>}
|
80
|
+
email2_raw = '+david@loudthinking.com'
|
81
|
+
email2_result = %{<a href="mailto:#{email2_raw}">#{email2_raw}</a>}
|
82
|
+
link_raw = 'http://www.rubyonrails.com'
|
83
|
+
link_result = %{<a href="#{link_raw}">#{link_raw}</a>}
|
84
|
+
link_result_with_options = %{<a href="#{link_raw}" target="_blank">#{link_raw}</a>}
|
85
|
+
link2_raw = 'www.rubyonrails.com'
|
86
|
+
link2_result = %{<a href="http://#{link2_raw}">#{link2_raw}</a>}
|
87
|
+
link3_raw = 'http://manuals.ruby-on-rails.com/read/chapter.need_a-period/103#page281'
|
88
|
+
link3_result = %{<a href="#{link3_raw}">#{link3_raw}</a>}
|
89
|
+
link4_raw = 'http://foo.example.com/controller/action?parm=value&p2=v2#anchor123'
|
90
|
+
link4_result = %{<a href="#{link4_raw}">#{CGI.escapeHTML(link4_raw)}</a>}
|
91
|
+
link5_raw = 'http://foo.example.com:3000/controller/action'
|
92
|
+
link5_result = %{<a href="#{link5_raw}">#{link5_raw}</a>}
|
93
|
+
link6_raw = 'http://foo.example.com:3000/controller/action+pack'
|
94
|
+
link6_result = %{<a href="#{link6_raw}">#{link6_raw}</a>}
|
95
|
+
link7_raw = 'http://foo.example.com/controller/action?parm=value&p2=v2#anchor-123'
|
96
|
+
link7_result = %{<a href="#{link7_raw}">#{CGI.escapeHTML(link7_raw)}</a>}
|
97
|
+
link8_raw = 'http://foo.example.com:3000/controller/action.html'
|
98
|
+
link8_result = %{<a href="#{link8_raw}">#{link8_raw}</a>}
|
99
|
+
link9_raw = 'http://business.timesonline.co.uk/article/0,,9065-2473189,00.html'
|
100
|
+
link9_result = %{<a href="#{link9_raw}">#{link9_raw}</a>}
|
101
|
+
link10_raw = 'http://www.mail-archive.com/ruby-talk@ruby-lang.org/'
|
102
|
+
link10_result = %{<a href="#{link10_raw}">#{link10_raw}</a>}
|
103
|
+
|
104
|
+
assert_linked %(Go to #{link_result} and say hello to #{email_result}), "Go to #{link_raw} and say hello to #{email_raw}"
|
105
|
+
assert_linked %(<p>Link #{link_result}</p>), "<p>Link #{link_raw}</p>"
|
106
|
+
assert_linked %(<p>#{link_result} Link</p>), "<p>#{link_raw} Link</p>"
|
107
|
+
assert_linked %(Go to #{link_result}.), %(Go to #{link_raw}.)
|
108
|
+
assert_linked %(<p>Go to #{link_result}, then say hello to #{email_result}.</p>), %(<p>Go to #{link_raw}, then say hello to #{email_raw}.</p>)
|
109
|
+
assert_linked %(<p>Link #{link2_result}</p>), "<p>Link #{link2_raw}</p>"
|
110
|
+
assert_linked %(<p>#{link2_result} Link</p>), "<p>#{link2_raw} Link</p>"
|
111
|
+
assert_linked %(Go to #{link2_result}.), %(Go to #{link2_raw}.)
|
112
|
+
assert_linked %(<p>Say hello to #{email_result}, then go to #{link2_result},</p>), %(<p>Say hello to #{email_raw}, then go to #{link2_raw},</p>)
|
113
|
+
assert_linked %(<p>Link #{link3_result}</p>), "<p>Link #{link3_raw}</p>"
|
114
|
+
assert_linked %(<p>#{link3_result} Link</p>), "<p>#{link3_raw} Link</p>"
|
115
|
+
assert_linked %(Go to #{link3_result}.), %(Go to #{link3_raw}.)
|
116
|
+
assert_linked %(<p>Go to #{link3_result}. seriously, #{link3_result}? i think I'll say hello to #{email_result}. instead.</p>), %(<p>Go to #{link3_raw}. seriously, #{link3_raw}? i think I'll say hello to #{email_raw}. instead.</p>)
|
117
|
+
assert_linked %(<p>Link #{link4_result}</p>), "<p>Link #{link4_raw}</p>"
|
118
|
+
assert_linked %(<p>#{link4_result} Link</p>), "<p>#{link4_raw} Link</p>"
|
119
|
+
assert_linked %(<p>#{link5_result} Link</p>), "<p>#{link5_raw} Link</p>"
|
120
|
+
assert_linked %(<p>#{link6_result} Link</p>), "<p>#{link6_raw} Link</p>"
|
121
|
+
assert_linked %(<p>#{link7_result} Link</p>), "<p>#{link7_raw} Link</p>"
|
122
|
+
assert_linked %(<p>Link #{link8_result}</p>), "<p>Link #{link8_raw}</p>"
|
123
|
+
assert_linked %(<p>#{link8_result} Link</p>), "<p>#{link8_raw} Link</p>"
|
124
|
+
assert_linked %(Go to #{link8_result}.), %(Go to #{link8_raw}.)
|
125
|
+
assert_linked %(<p>Go to #{link8_result}. seriously, #{link8_result}? i think I'll say hello to #{email_result}. instead.</p>), %(<p>Go to #{link8_raw}. seriously, #{link8_raw}? i think I'll say hello to #{email_raw}. instead.</p>)
|
126
|
+
assert_linked %(<p>Link #{link9_result}</p>), "<p>Link #{link9_raw}</p>"
|
127
|
+
assert_linked %(<p>#{link9_result} Link</p>), "<p>#{link9_raw} Link</p>"
|
128
|
+
assert_linked %(Go to #{link9_result}.), %(Go to #{link9_raw}.)
|
129
|
+
assert_linked %(<p>Go to #{link9_result}. seriously, #{link9_result}? i think I'll say hello to #{email_result}. instead.</p>), %(<p>Go to #{link9_raw}. seriously, #{link9_raw}? i think I'll say hello to #{email_raw}. instead.</p>)
|
130
|
+
assert_linked %(<p>#{link10_result} Link</p>), "<p>#{link10_raw} Link</p>"
|
131
|
+
assert_linked email2_result, email2_raw
|
132
|
+
assert_linked "#{link_result} #{link_result} #{link_result}", "#{link_raw} #{link_raw} #{link_raw}"
|
133
|
+
assert_linked '<a href="http://www.rubyonrails.com">Ruby On Rails</a>', '<a href="http://www.rubyonrails.com">Ruby On Rails</a>'
|
134
|
+
end
|
135
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rinku
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- "Vicent Mart\xC3\xAD"
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-06-09 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: " A fast and very smart autolinking library that\n acts as a drop-in replacement for Rails `auto_link`\n"
|
23
|
+
email: vicent@github.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions:
|
27
|
+
- ext/rinku/extconf.rb
|
28
|
+
extra_rdoc_files:
|
29
|
+
- COPYING
|
30
|
+
files:
|
31
|
+
- COPYING
|
32
|
+
- VERSION
|
33
|
+
- README.markdown
|
34
|
+
- Rakefile
|
35
|
+
- ext/rinku/rinku.c
|
36
|
+
- ext/rinku/autolink.c
|
37
|
+
- ext/rinku/autolink.h
|
38
|
+
- ext/rinku/buffer.c
|
39
|
+
- ext/rinku/buffer.h
|
40
|
+
- ext/rinku/html_autolink.c
|
41
|
+
- ext/rinku/extconf.rb
|
42
|
+
- lib/rinku.rb
|
43
|
+
- rinku.gemspec
|
44
|
+
- test/autolink_test.rb
|
45
|
+
has_rdoc: true
|
46
|
+
homepage: http://github.com/tanoku/rinku
|
47
|
+
licenses: []
|
48
|
+
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options: []
|
51
|
+
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
hash: 3
|
69
|
+
segments:
|
70
|
+
- 0
|
71
|
+
version: "0"
|
72
|
+
requirements: []
|
73
|
+
|
74
|
+
rubyforge_project:
|
75
|
+
rubygems_version: 1.3.7
|
76
|
+
signing_key:
|
77
|
+
specification_version: 3
|
78
|
+
summary: Mostly autolinking
|
79
|
+
test_files:
|
80
|
+
- test/autolink_test.rb
|