rinku 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +64 -43
- data/ext/rinku/autolink.c +38 -16
- data/ext/rinku/autolink.h +4 -1
- data/ext/rinku/html_autolink.c +61 -56
- data/ext/rinku/rinku.c +82 -12
- data/lib/rails_rinku.rb +35 -0
- data/lib/rinku.rb +1 -37
- data/rinku.gemspec +2 -2
- data/test/autolink_test.rb +91 -8
- metadata +7 -9
- data/VERSION +0 -1
data/README.markdown
CHANGED
@@ -18,52 +18,73 @@ The Rinku source is available at GitHub:
|
|
18
18
|
|
19
19
|
$ git clone git://github.com/tanoku/rinku.git
|
20
20
|
|
21
|
-
Rinku is a
|
22
|
-
|
21
|
+
Rinku is a standalone library
|
22
|
+
-----------------------------
|
23
|
+
|
24
|
+
It exports a single method called `Rinku.auto_link`.
|
25
|
+
|
26
|
+
~~~~ruby
|
27
|
+
require 'rinku'
|
23
28
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
~~~~~~ruby
|
28
|
-
# Turns all URLs and e-mail addresses into clickable links. The <tt>:link</tt> option
|
29
|
-
# will limit what should be linked. You can add HTML attributes to the links using
|
30
|
-
# <tt>:html</tt>. Possible values for <tt>:link</tt> are <tt>:all</tt> (default),
|
31
|
-
# <tt>:email_addresses</tt>, and <tt>:urls</tt>. If a block is given, each URL and
|
32
|
-
# e-mail address is yielded and the result is used as the link text.
|
33
|
-
#
|
34
|
-
# ==== Examples
|
35
|
-
# auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com")
|
36
|
-
# # => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> and
|
37
|
-
# # say hello to <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
|
38
|
-
#
|
39
|
-
# auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :urls)
|
40
|
-
# # => "Visit <a href=\"http://www.loudthinking.com/\">http://www.loudthinking.com/</a>
|
41
|
-
# # or e-mail david@loudthinking.com"
|
42
|
-
#
|
43
|
-
# auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :email_addresses)
|
44
|
-
# # => "Visit http://www.loudthinking.com/ or e-mail <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
|
45
|
-
#
|
46
|
-
# post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com."
|
47
|
-
# auto_link(post_body, :html => { :target => '_blank' }) do |text|
|
48
|
-
# truncate(text, :length => 15)
|
49
|
-
# end
|
50
|
-
# # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.
|
51
|
-
# Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
|
52
|
-
#
|
53
|
-
#
|
54
|
-
# You can still use <tt>auto_link</tt> with the old API that accepts the
|
55
|
-
# +link+ as its optional second parameter and the +html_options+ hash
|
56
|
-
# as its optional third parameter:
|
57
|
-
# post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com."
|
58
|
-
# auto_link(post_body, :urls) # => Once upon\na time
|
59
|
-
# # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\">http://www.myblog.com</a>.
|
60
|
-
# Please e-mail me at me@email.com."
|
61
|
-
#
|
62
|
-
# auto_link(post_body, :all, :target => "_blank") # => Once upon\na time
|
63
|
-
# # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.myblog.com</a>.
|
64
|
-
# Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
|
29
|
+
auto_link(text, mode=:all, link_attr=nil)
|
30
|
+
auto_link(text, mode=:all, link_attr=nil) { |link_text| ... }
|
65
31
|
~~~~~~~~~
|
66
32
|
|
33
|
+
Parses a block of text looking for "safe" urls or email addresses,
|
34
|
+
and turns them into HTML links with the given attributes.
|
35
|
+
|
36
|
+
NOTE: Currently the follow protocols are considered safe and are the
|
37
|
+
only ones that will be autolinked.
|
38
|
+
|
39
|
+
http:// https:// ftp:// mailto://
|
40
|
+
|
41
|
+
Email addresses are also autolinked by default. URLs without a protocol
|
42
|
+
specifier but starting with `www.` will also be autolinked, defaulting to
|
43
|
+
the `http://` protocol.
|
44
|
+
|
45
|
+
- `text` is a string in plain text or HTML markup. If the string is formatted in
|
46
|
+
HTML, Rinku is smart enough to skip the links that are already enclosed in <a>
|
47
|
+
tags.
|
48
|
+
|
49
|
+
- `mode` is a symbol, either :all, :urls or :email_addresses, which specifies which
|
50
|
+
kind of links will be auto-linked.
|
51
|
+
|
52
|
+
- `link_attr` is a string containing the link attributes for each link that
|
53
|
+
will be generated. These attributes are not sanitized and will be include as-is
|
54
|
+
in each generated link, e.g.
|
55
|
+
|
56
|
+
~~~~ruby
|
57
|
+
auto_link('http://www.pokemon.com', :all, 'target="_blank"')
|
58
|
+
# => '<a href="http://www.pokemon.com" target="_blank">http://www.pokemon.com</a>'
|
59
|
+
~~~~
|
60
|
+
|
61
|
+
This string can be autogenerated from a hash using the Rails `tag_options` helper.
|
62
|
+
|
63
|
+
- The method takes an optional block argument. If a block is passed, it will
|
64
|
+
be yielded for each found link in the text, and its return value will be used instead
|
65
|
+
of the name of the link. E.g.
|
66
|
+
|
67
|
+
~~~~ruby
|
68
|
+
auto_link('Check it out at http://www.pokemon.com') do |url|
|
69
|
+
"THE POKEMAN WEBSITEZ"
|
70
|
+
end
|
71
|
+
# => 'Check it out at <a href="http://www.pokemon.com">THE POKEMAN WEBSITEZ</a>'
|
72
|
+
~~~~
|
73
|
+
|
74
|
+
Rinku is a drop-in replacement for Rails 3.1 `auto_link`
|
75
|
+
----------------------------------------------------
|
76
|
+
|
77
|
+
Auto-linking functionality has been removed from Rails 3.1,
|
78
|
+
and is instead offered as a standalone gem, `rails_autolink`. You can
|
79
|
+
choose to use Rinku instead.
|
80
|
+
|
81
|
+
~~~~ruby
|
82
|
+
require 'rails_rinku'
|
83
|
+
~~~~
|
84
|
+
|
85
|
+
The `rails_rinku` package monkeypatches Rails with an `auto_link` method that
|
86
|
+
mimics 100% the original one, parameter per parameter. It's just faster.
|
87
|
+
|
67
88
|
Rinku is written by me
|
68
89
|
----------------------
|
69
90
|
|
data/ext/rinku/autolink.c
CHANGED
@@ -47,6 +47,13 @@ static size_t
|
|
47
47
|
autolink_delim(char *data, size_t link_end, size_t offset, size_t size)
|
48
48
|
{
|
49
49
|
char cclose, copen = 0;
|
50
|
+
size_t i;
|
51
|
+
|
52
|
+
for (i = 0; i < link_end; ++i)
|
53
|
+
if (data[i] == '<') {
|
54
|
+
link_end = i;
|
55
|
+
break;
|
56
|
+
}
|
50
57
|
|
51
58
|
while (link_end > 0) {
|
52
59
|
if (strchr("?!.,", data[link_end - 1]) != NULL)
|
@@ -63,11 +70,6 @@ autolink_delim(char *data, size_t link_end, size_t offset, size_t size)
|
|
63
70
|
else
|
64
71
|
link_end--;
|
65
72
|
}
|
66
|
-
|
67
|
-
else if (data[link_end - 1] == '>') {
|
68
|
-
while (link_end > 0 && data[link_end] != '<')
|
69
|
-
link_end--;
|
70
|
-
}
|
71
73
|
else break;
|
72
74
|
}
|
73
75
|
|
@@ -125,11 +127,29 @@ autolink_delim(char *data, size_t link_end, size_t offset, size_t size)
|
|
125
127
|
return link_end;
|
126
128
|
}
|
127
129
|
|
130
|
+
static size_t
|
131
|
+
check_domain(char *data, size_t size)
|
132
|
+
{
|
133
|
+
size_t i, np = 0;
|
134
|
+
|
135
|
+
if (!isalnum(data[0]))
|
136
|
+
return 0;
|
137
|
+
|
138
|
+
for (i = 1; i < size - 1; ++i) {
|
139
|
+
if (data[i] == '.') np++;
|
140
|
+
else if (!isalnum(data[i])) break;
|
141
|
+
}
|
142
|
+
|
143
|
+
if (!isalnum(data[i - 1]) || np == 0)
|
144
|
+
return 0;
|
145
|
+
|
146
|
+
return i;
|
147
|
+
}
|
148
|
+
|
128
149
|
size_t
|
129
150
|
ups_autolink__www(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size)
|
130
151
|
{
|
131
152
|
size_t link_end;
|
132
|
-
int np = 0;
|
133
153
|
|
134
154
|
if (offset > 0 && !ispunct(data[-1]) && !isspace(data[-1]))
|
135
155
|
return 0;
|
@@ -137,17 +157,14 @@ ups_autolink__www(size_t *rewind_p, struct buf *link, char *data, size_t offset,
|
|
137
157
|
if (size < 4 || memcmp(data, "www.", STRLEN("www.")) != 0)
|
138
158
|
return 0;
|
139
159
|
|
140
|
-
link_end =
|
141
|
-
while (link_end < size && !isspace(data[link_end])) {
|
142
|
-
if (data[link_end] == '.')
|
143
|
-
np++;
|
144
|
-
|
145
|
-
link_end++;
|
146
|
-
}
|
160
|
+
link_end = check_domain(data, size);
|
147
161
|
|
148
|
-
if (
|
162
|
+
if (link_end == 0)
|
149
163
|
return 0;
|
150
164
|
|
165
|
+
while (link_end < size && !isspace(data[link_end]))
|
166
|
+
link_end++;
|
167
|
+
|
151
168
|
link_end = autolink_delim(data, link_end, offset, size);
|
152
169
|
|
153
170
|
if (link_end == 0)
|
@@ -211,7 +228,7 @@ ups_autolink__email(size_t *rewind_p, struct buf *link, char *data, size_t offse
|
|
211
228
|
size_t
|
212
229
|
ups_autolink__url(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size)
|
213
230
|
{
|
214
|
-
size_t link_end, rewind = 0;
|
231
|
+
size_t link_end, rewind = 0, domain_len;
|
215
232
|
|
216
233
|
if (size < 4 || data[1] != '/' || data[2] != '/')
|
217
234
|
return 0;
|
@@ -221,8 +238,13 @@ ups_autolink__url(size_t *rewind_p, struct buf *link, char *data, size_t offset,
|
|
221
238
|
|
222
239
|
if (!is_safe_link(data - rewind, size + rewind))
|
223
240
|
return 0;
|
241
|
+
link_end = STRLEN("://");
|
242
|
+
|
243
|
+
domain_len = check_domain(data + link_end, size - link_end);
|
244
|
+
if (domain_len == 0)
|
245
|
+
return 0;
|
224
246
|
|
225
|
-
link_end
|
247
|
+
link_end += domain_len;
|
226
248
|
while (link_end < size && !isspace(data[link_end]))
|
227
249
|
link_end++;
|
228
250
|
|
data/ext/rinku/autolink.h
CHANGED
@@ -22,7 +22,10 @@
|
|
22
22
|
typedef enum {
|
23
23
|
AUTOLINK_URLS = (1 << 0),
|
24
24
|
AUTOLINK_EMAILS = (1 << 1),
|
25
|
-
AUTOLINK_ALL = AUTOLINK_URLS|AUTOLINK_EMAILS
|
25
|
+
AUTOLINK_ALL = AUTOLINK_URLS|AUTOLINK_EMAILS,
|
26
|
+
AUTOLINK_SANITIZE_NAMES = (1 << 2),
|
27
|
+
AUTOLINK_SANITIZE_URLS = (1 << 3),
|
28
|
+
AUTOLINK_SANITIZE = AUTOLINK_SANITIZE_NAMES|AUTOLINK_SANITIZE_URLS,
|
26
29
|
} autolink_mode;
|
27
30
|
|
28
31
|
extern size_t
|
data/ext/rinku/html_autolink.c
CHANGED
@@ -83,7 +83,7 @@ is_closing_a(const char *tag, size_t size)
|
|
83
83
|
}
|
84
84
|
|
85
85
|
static size_t
|
86
|
-
|
86
|
+
autolink__skip_tag(struct buf *ob, char *text, size_t size)
|
87
87
|
{
|
88
88
|
size_t i = 0;
|
89
89
|
|
@@ -105,6 +105,30 @@ skip_tags(struct buf *ob, const char *text, size_t size)
|
|
105
105
|
return i + 1;
|
106
106
|
}
|
107
107
|
|
108
|
+
typedef size_t (*autolink_parse_cb)(size_t *rewind, struct buf *, char *, size_t, size_t);
|
109
|
+
|
110
|
+
typedef enum {
|
111
|
+
AUTOLINK_ACTION_NONE = 0,
|
112
|
+
AUTOLINK_ACTION_WWW,
|
113
|
+
AUTOLINK_ACTION_EMAIL,
|
114
|
+
AUTOLINK_ACTION_URL,
|
115
|
+
AUTOLINK_ACTION_SKIP_TAG
|
116
|
+
} autolink_action;
|
117
|
+
|
118
|
+
static autolink_parse_cb g_callbacks[] = {
|
119
|
+
NULL,
|
120
|
+
ups_autolink__www, /* 1 */
|
121
|
+
ups_autolink__email,/* 2 */
|
122
|
+
ups_autolink__url, /* 3 */
|
123
|
+
};
|
124
|
+
|
125
|
+
static const char *g_hrefs[] = {
|
126
|
+
NULL,
|
127
|
+
"<a href=\"http://",
|
128
|
+
"<a href=\"mailto:",
|
129
|
+
"<a href=\"",
|
130
|
+
};
|
131
|
+
|
108
132
|
void
|
109
133
|
upshtml_autolink(
|
110
134
|
struct buf *ob,
|
@@ -116,38 +140,42 @@ upshtml_autolink(
|
|
116
140
|
{
|
117
141
|
size_t i, end;
|
118
142
|
struct buf *link = bufnew(16);
|
119
|
-
|
143
|
+
char active_chars[256];
|
144
|
+
void (*link_url_cb)(struct buf *, const struct buf *, void *);
|
120
145
|
|
121
146
|
if (!text || text->size == 0)
|
122
147
|
return;
|
123
148
|
|
124
|
-
|
125
|
-
case AUTOLINK_EMAILS:
|
126
|
-
active_chars = "<@";
|
127
|
-
break;
|
149
|
+
memset(active_chars, 0x0, sizeof(active_chars));
|
128
150
|
|
129
|
-
|
130
|
-
active_chars = "<w:";
|
151
|
+
active_chars['<'] = AUTOLINK_ACTION_SKIP_TAG;
|
131
152
|
|
132
|
-
|
133
|
-
|
134
|
-
break;
|
153
|
+
if (flags & AUTOLINK_EMAILS)
|
154
|
+
active_chars['@'] = AUTOLINK_ACTION_EMAIL;
|
135
155
|
|
136
|
-
|
137
|
-
|
156
|
+
if (flags & AUTOLINK_URLS) {
|
157
|
+
active_chars['w'] = AUTOLINK_ACTION_WWW;
|
158
|
+
active_chars['W'] = AUTOLINK_ACTION_WWW;
|
159
|
+
active_chars[':'] = AUTOLINK_ACTION_URL;
|
138
160
|
}
|
139
161
|
|
140
162
|
if (link_text_cb == NULL)
|
141
163
|
link_text_cb = &autolink_escape_cb;
|
142
164
|
|
165
|
+
if (link_attr != NULL) {
|
166
|
+
while (isspace(*link_attr))
|
167
|
+
link_attr++;
|
168
|
+
}
|
169
|
+
|
143
170
|
bufgrow(ob, text->size);
|
144
171
|
|
145
172
|
i = end = 0;
|
146
173
|
|
147
174
|
while (i < text->size) {
|
148
175
|
size_t rewind;
|
176
|
+
char action;
|
149
177
|
|
150
|
-
while (end < text->size &&
|
178
|
+
while (end < text->size && (action = active_chars[(int)text->data[end]]) == 0)
|
151
179
|
end++;
|
152
180
|
|
153
181
|
bufput(ob, text->data + i, end - i);
|
@@ -156,57 +184,32 @@ upshtml_autolink(
|
|
156
184
|
break;
|
157
185
|
|
158
186
|
i = end;
|
187
|
+
end = 0;
|
159
188
|
link->size = 0;
|
160
189
|
|
161
|
-
|
162
|
-
|
163
|
-
|
190
|
+
if (action == AUTOLINK_ACTION_SKIP_TAG) {
|
191
|
+
end = autolink__skip_tag(ob, text->data + i, text->size - i);
|
192
|
+
} else {
|
193
|
+
end = g_callbacks[(int)action](&rewind, link, text->data + i, i, text->size - i);
|
194
|
+
|
195
|
+
/* print the link */
|
164
196
|
if (end > 0) {
|
165
197
|
ob->size -= rewind;
|
166
|
-
BUFPUTSL(ob, "<a");
|
167
|
-
if (link_attr) bufputs(ob, link_attr);
|
168
|
-
BUFPUTSL(ob, " href=\"mailto:");
|
169
|
-
bufput(ob, link->data, link->size);
|
170
|
-
BUFPUTSL(ob, "\">");
|
171
|
-
link_text_cb(ob, link, payload);
|
172
|
-
BUFPUTSL(ob, "</a>");
|
173
|
-
}
|
174
|
-
break;
|
175
198
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
}
|
187
|
-
break;
|
199
|
+
bufputs(ob, g_hrefs[(int)action]);
|
200
|
+
autolink_escape_cb(ob, link, NULL);
|
201
|
+
|
202
|
+
if (link_attr) {
|
203
|
+
BUFPUTSL(ob, "\" ");
|
204
|
+
bufputs(ob, link_attr);
|
205
|
+
bufputc(ob, '>');
|
206
|
+
} else {
|
207
|
+
BUFPUTSL(ob, "\">");
|
208
|
+
}
|
188
209
|
|
189
|
-
case ':':
|
190
|
-
end = ups_autolink__url(&rewind, link, text->data + i, i, text->size - i);
|
191
|
-
if (end > 0) {
|
192
|
-
ob->size -= rewind;
|
193
|
-
BUFPUTSL(ob, "<a");
|
194
|
-
if (link_attr) bufputs(ob, link_attr);
|
195
|
-
BUFPUTSL(ob, " href=\"");
|
196
|
-
bufput(ob, link->data, link->size);
|
197
|
-
BUFPUTSL(ob, "\">");
|
198
210
|
link_text_cb(ob, link, payload);
|
199
211
|
BUFPUTSL(ob, "</a>");
|
200
212
|
}
|
201
|
-
break;
|
202
|
-
|
203
|
-
case '<':
|
204
|
-
end = skip_tags(ob, text->data + i, text->size - i);
|
205
|
-
break;
|
206
|
-
|
207
|
-
default:
|
208
|
-
end = 0;
|
209
|
-
break;
|
210
213
|
}
|
211
214
|
|
212
215
|
if (!end)
|
@@ -216,6 +219,8 @@ upshtml_autolink(
|
|
216
219
|
end = i;
|
217
220
|
}
|
218
221
|
}
|
222
|
+
|
223
|
+
bufrelease(link);
|
219
224
|
}
|
220
225
|
|
221
226
|
|
data/ext/rinku/rinku.c
CHANGED
@@ -1,6 +1,29 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2011, Vicent Marti
|
3
|
+
*
|
4
|
+
* Permission to use, copy, modify, and distribute this software for any
|
5
|
+
* purpose with or without fee is hereby granted, provided that the above
|
6
|
+
* copyright notice and this permission notice appear in all copies.
|
7
|
+
*
|
8
|
+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
9
|
+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
10
|
+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
11
|
+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
12
|
+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
13
|
+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
14
|
+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
15
|
+
*/
|
16
|
+
#define RSTRING_NOT_MODIFIED
|
17
|
+
|
1
18
|
#include <stdio.h>
|
2
19
|
#include "ruby.h"
|
3
20
|
|
21
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
22
|
+
#include <ruby/encoding.h>
|
23
|
+
#else
|
24
|
+
#define rb_enc_copy(dst, src)
|
25
|
+
#endif
|
26
|
+
|
4
27
|
#include "autolink.h"
|
5
28
|
#include "buffer.h"
|
6
29
|
|
@@ -25,6 +48,50 @@ autolink_callback(struct buf *link_text, const struct buf *link, void *block)
|
|
25
48
|
bufput(link_text, RSTRING_PTR(rb_link_text), RSTRING_LEN(rb_link_text));
|
26
49
|
}
|
27
50
|
|
51
|
+
/*
|
52
|
+
* Document-method: auto_link
|
53
|
+
*
|
54
|
+
* call-seq:
|
55
|
+
* auto_link(text, mode=:all, link_attr=nil)
|
56
|
+
* auto_link(text, mode=:all, link_attr=nil) { |link_text| ... }
|
57
|
+
*
|
58
|
+
* Parses a block of text looking for "safe" urls or email addresses,
|
59
|
+
* and turns them into HTML links with the given attributes.
|
60
|
+
*
|
61
|
+
* NOTE: Currently the follow protocols are considered safe and are the
|
62
|
+
* only ones that will be autolinked.
|
63
|
+
*
|
64
|
+
* http:// https:// ftp:// mailto://
|
65
|
+
*
|
66
|
+
* Email addresses are also autolinked by default. URLs without a protocol
|
67
|
+
* specifier but starting with 'www.' will also be autolinked, defaulting to
|
68
|
+
* the 'http://' protocol.
|
69
|
+
*
|
70
|
+
* +text+ is a string in plain text or HTML markup. If the string is formatted in
|
71
|
+
* HTML, Rinku is smart enough to skip the links that are already enclosed in <a>
|
72
|
+
* tags.
|
73
|
+
*
|
74
|
+
* +mode+ is a symbol, either :all, :urls or :email_addresses, which specifies which
|
75
|
+
* kind of links will be auto-linked.
|
76
|
+
*
|
77
|
+
* +link_attr+ is a string containing the link attributes for each link that
|
78
|
+
* will be generated. These attributes are not sanitized and will be include as-is
|
79
|
+
* in each generated link, e.g.
|
80
|
+
*
|
81
|
+
* auto_link('http://www.pokemon.com', :all, 'target="_blank"')
|
82
|
+
* # => '<a href="http://www.pokemon.com" target="_blank">http://www.pokemon.com</a>'
|
83
|
+
*
|
84
|
+
* This string can be autogenerated from a hash using the Rails `tag_options` helper.
|
85
|
+
*
|
86
|
+
* +block+ The method takes an optional block argument. If a block is passed, it will
|
87
|
+
* be yielded for each found link in the text, and its return value will be used instead
|
88
|
+
* of the name of the link. E.g.
|
89
|
+
*
|
90
|
+
* auto_link('Check it out at http://www.pokemon.com') do |url|
|
91
|
+
* "THE POKEMAN WEBSITEZ"
|
92
|
+
* end
|
93
|
+
* # => 'Check it out at <a href="http://www.pokemon.com">THE POKEMAN WEBSITEZ</a>'
|
94
|
+
*/
|
28
95
|
static VALUE
|
29
96
|
rb_rinku_autolink(int argc, VALUE *argv, VALUE self)
|
30
97
|
{
|
@@ -34,10 +101,16 @@ rb_rinku_autolink(int argc, VALUE *argv, VALUE self)
|
|
34
101
|
const char *link_attr = NULL;
|
35
102
|
ID mode_sym;
|
36
103
|
|
37
|
-
rb_scan_args(argc, argv, "
|
104
|
+
rb_scan_args(argc, argv, "12&", &rb_text, &rb_mode, &rb_html, &rb_block);
|
38
105
|
|
39
106
|
Check_Type(rb_text, T_STRING);
|
40
|
-
|
107
|
+
|
108
|
+
if (!NIL_P(rb_mode)) {
|
109
|
+
Check_Type(rb_mode, T_SYMBOL);
|
110
|
+
mode_sym = SYM2ID(rb_mode);
|
111
|
+
} else {
|
112
|
+
mode_sym = rb_intern("all");
|
113
|
+
}
|
41
114
|
|
42
115
|
if (!NIL_P(rb_html)) {
|
43
116
|
Check_Type(rb_html, T_STRING);
|
@@ -48,8 +121,6 @@ rb_rinku_autolink(int argc, VALUE *argv, VALUE self)
|
|
48
121
|
input_buf.size = RSTRING_LEN(rb_text);
|
49
122
|
output_buf = bufnew(128);
|
50
123
|
|
51
|
-
mode_sym = SYM2ID(rb_mode);
|
52
|
-
|
53
124
|
if (mode_sym == rb_intern("all"))
|
54
125
|
link_mode = AUTOLINK_ALL;
|
55
126
|
else if (mode_sym == rb_intern("email_addresses"))
|
@@ -60,27 +131,26 @@ rb_rinku_autolink(int argc, VALUE *argv, VALUE self)
|
|
60
131
|
rb_raise(rb_eTypeError,
|
61
132
|
"Invalid linking mode (possible values are :all, :urls, :email_addresses)");
|
62
133
|
|
134
|
+
link_mode |= AUTOLINK_SANITIZE;
|
135
|
+
|
63
136
|
if (RTEST(rb_block))
|
64
|
-
upshtml_autolink(output_buf, &input_buf,
|
137
|
+
upshtml_autolink(output_buf, &input_buf, link_mode, link_attr, &autolink_callback, (void*)rb_block);
|
65
138
|
else
|
66
|
-
upshtml_autolink(output_buf, &input_buf,
|
139
|
+
upshtml_autolink(output_buf, &input_buf, link_mode, link_attr, NULL, NULL);
|
67
140
|
|
68
141
|
result = rb_str_new(output_buf->data, output_buf->size);
|
69
142
|
bufrelease(output_buf);
|
70
143
|
|
71
144
|
/* force the input encoding */
|
72
|
-
|
73
|
-
VALUE encoding = rb_funcall(rb_text, rb_intern("encoding"), 0);
|
74
|
-
rb_funcall(result, rb_intern("force_encoding"), 1, encoding);
|
75
|
-
}
|
145
|
+
rb_enc_copy(result, rb_text);
|
76
146
|
|
77
147
|
return result;
|
78
148
|
}
|
79
149
|
|
80
150
|
void Init_rinku()
|
81
151
|
{
|
82
|
-
|
83
|
-
rb_define_singleton_method(rb_cRinku, "
|
152
|
+
rb_cRinku = rb_define_class("Rinku", rb_cObject);
|
153
|
+
rb_define_singleton_method(rb_cRinku, "auto_link", rb_rinku_autolink, -1);
|
84
154
|
}
|
85
155
|
|
86
156
|
|
data/lib/rails_rinku.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rinku'
|
2
|
+
|
3
|
+
module RailsRinku
|
4
|
+
class Railtie < ::Rails::Railtie
|
5
|
+
initializer 'rails_rinku' do |app|
|
6
|
+
ActiveSupport.on_load(:action_view) do
|
7
|
+
require 'active_support/core_ext/object/blank'
|
8
|
+
require 'active_support/core_ext/array/extract_options'
|
9
|
+
require 'active_support/core_ext/hash/reverse_merge'
|
10
|
+
require 'active_support/core_ext/hash/keys'
|
11
|
+
|
12
|
+
module ::ActionView
|
13
|
+
module Helpers # :nodoc:
|
14
|
+
module TextHelper
|
15
|
+
def auto_link(text, *args, &block)
|
16
|
+
return ''.html_safe if text.blank?
|
17
|
+
|
18
|
+
options = args.size == 2 ? {} : args.extract_options!
|
19
|
+
unless args.empty?
|
20
|
+
options[:link] = args[0] || :all
|
21
|
+
options[:html] = args[1] || {}
|
22
|
+
end
|
23
|
+
options.reverse_merge!(:link => :all, :html => {})
|
24
|
+
text = sanitize(text) unless options[:sanitize] == false
|
25
|
+
|
26
|
+
Rinku.auto_link(text, options[:link], tag_options(options[:html]), &block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
data/lib/rinku.rb
CHANGED
@@ -1,41 +1,5 @@
|
|
1
|
-
require 'set'
|
2
|
-
require 'cgi'
|
3
|
-
|
4
1
|
class Rinku
|
5
|
-
|
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
|
2
|
+
VERSION = "1.1.0"
|
39
3
|
end
|
40
4
|
|
41
5
|
require 'rinku.so'
|
data/rinku.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'rinku'
|
3
|
-
s.version =
|
3
|
+
s.version = '1.1.0'
|
4
4
|
s.summary = "Mostly autolinking"
|
5
5
|
s.description = <<-EOF
|
6
6
|
A fast and very smart autolinking library that
|
@@ -12,7 +12,6 @@ Gem::Specification.new do |s|
|
|
12
12
|
# = MANIFEST =
|
13
13
|
s.files = %w[
|
14
14
|
COPYING
|
15
|
-
VERSION
|
16
15
|
README.markdown
|
17
16
|
Rakefile
|
18
17
|
ext/rinku/rinku.c
|
@@ -23,6 +22,7 @@ Gem::Specification.new do |s|
|
|
23
22
|
ext/rinku/html_autolink.c
|
24
23
|
ext/rinku/extconf.rb
|
25
24
|
lib/rinku.rb
|
25
|
+
lib/rails_rinku.rb
|
26
26
|
rinku.gemspec
|
27
27
|
test/autolink_test.rb
|
28
28
|
]
|
data/test/autolink_test.rb
CHANGED
@@ -11,6 +11,74 @@ class RedcarpetAutolinkTest < Test::Unit::TestCase
|
|
11
11
|
assert_equal expected, Rinku.auto_link(url)
|
12
12
|
end
|
13
13
|
|
14
|
+
def test_auto_link_with_brackets
|
15
|
+
link1_raw = 'http://en.wikipedia.org/wiki/Sprite_(computer_graphics)'
|
16
|
+
link1_result = generate_result(link1_raw)
|
17
|
+
assert_equal link1_result, Rinku.auto_link(link1_raw)
|
18
|
+
assert_equal "(link: #{link1_result})", Rinku.auto_link("(link: #{link1_raw})")
|
19
|
+
|
20
|
+
link2_raw = 'http://en.wikipedia.org/wiki/Sprite_[computer_graphics]'
|
21
|
+
link2_result = generate_result(link2_raw)
|
22
|
+
assert_equal link2_result, Rinku.auto_link(link2_raw)
|
23
|
+
assert_equal "[link: #{link2_result}]", Rinku.auto_link("[link: #{link2_raw}]")
|
24
|
+
|
25
|
+
link3_raw = 'http://en.wikipedia.org/wiki/Sprite_{computer_graphics}'
|
26
|
+
link3_result = generate_result(link3_raw)
|
27
|
+
assert_equal link3_result, Rinku.auto_link(link3_raw)
|
28
|
+
assert_equal "{link: #{link3_result}}", Rinku.auto_link("{link: #{link3_raw}}")
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_auto_link_with_multiple_trailing_punctuations
|
32
|
+
url = "http://youtube.com"
|
33
|
+
url_result = generate_result(url)
|
34
|
+
assert_equal url_result, Rinku.auto_link(url)
|
35
|
+
assert_equal "(link: #{url_result}).", Rinku.auto_link("(link: #{url}).")
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_auto_link_with_block
|
39
|
+
url = "http://api.rubyonrails.com/Foo.html"
|
40
|
+
email = "fantabulous@shiznadel.ic"
|
41
|
+
|
42
|
+
assert_equal %(<p><a href="#{url}">#{url[0...7]}...</a><br /><a href="mailto:#{email}">#{email[0...7]}...</a><br /></p>), Rinku.auto_link("<p>#{url}<br />#{email}<br /></p>") { |_url| _url[0...7] + '...'}
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_auto_link_with_block_with_html
|
46
|
+
pic = "http://example.com/pic.png"
|
47
|
+
url = "http://example.com/album?a&b=c"
|
48
|
+
|
49
|
+
assert_equal %(My pic: <a href="#{pic}"><img src="#{pic}" width="160px"></a> -- full album here #{generate_result(url)}), Rinku.auto_link("My pic: #{pic} -- full album here #{url}") { |link|
|
50
|
+
if link =~ /\.(jpg|gif|png|bmp|tif)$/i
|
51
|
+
%(<img src="#{CGI.escapeHTML link}" width="160px">)
|
52
|
+
else
|
53
|
+
CGI.escapeHTML link
|
54
|
+
end
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_auto_link_already_linked
|
59
|
+
linked1 = generate_result('Ruby On Rails', 'http://www.rubyonrails.com')
|
60
|
+
linked2 = %('<a href="http://www.example.com">www.example.com</a>')
|
61
|
+
linked3 = %('<a href="http://www.example.com" rel="nofollow">www.example.com</a>')
|
62
|
+
linked4 = %('<a href="http://www.example.com"><b>www.example.com</b></a>')
|
63
|
+
linked5 = %('<a href="#close">close</a> <a href="http://www.example.com"><b>www.example.com</b></a>')
|
64
|
+
assert_equal linked1, Rinku.auto_link(linked1)
|
65
|
+
assert_equal linked2, Rinku.auto_link(linked2)
|
66
|
+
assert_equal linked3, Rinku.auto_link(linked3)
|
67
|
+
assert_equal linked4, Rinku.auto_link(linked4)
|
68
|
+
assert_equal linked5, Rinku.auto_link(linked5)
|
69
|
+
|
70
|
+
linked_email = %Q(<a href="mailto:david@loudthinking.com">Mail me</a>)
|
71
|
+
assert_equal linked_email, Rinku.auto_link(linked_email)
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def test_auto_link_at_eol
|
76
|
+
url1 = "http://api.rubyonrails.com/Foo.html"
|
77
|
+
url2 = "http://www.ruby-doc.org/core/Bar.html"
|
78
|
+
|
79
|
+
assert_equal %(<p><a href="#{url1}">#{url1}</a><br /><a href="#{url2}">#{url2}</a><br /></p>), Rinku.auto_link("<p>#{url1}<br />#{url2}<br /></p>")
|
80
|
+
end
|
81
|
+
|
14
82
|
def test_block
|
15
83
|
link = Rinku.auto_link("Find ur favorite pokeman @ http://www.pokemon.com") do |url|
|
16
84
|
assert_equal url, "http://www.pokemon.com"
|
@@ -20,11 +88,6 @@ class RedcarpetAutolinkTest < Test::Unit::TestCase
|
|
20
88
|
assert_equal link, "Find ur favorite pokeman @ <a href=\"http://www.pokemon.com\">POKEMAN WEBSITE</a>"
|
21
89
|
end
|
22
90
|
|
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
91
|
def test_autolink_works
|
29
92
|
url = "http://example.com/"
|
30
93
|
assert_linked "<a href=\"#{url}\">#{url}</a>", url
|
@@ -70,7 +133,7 @@ class RedcarpetAutolinkTest < Test::Unit::TestCase
|
|
70
133
|
)
|
71
134
|
|
72
135
|
urls.each do |url|
|
73
|
-
assert_linked %(<a href="#{url}">#{CGI.escapeHTML
|
136
|
+
assert_linked %(<a href="#{CGI.escapeHTML url}">#{CGI.escapeHTML url}</a>), url
|
74
137
|
end
|
75
138
|
end
|
76
139
|
|
@@ -87,13 +150,13 @@ class RedcarpetAutolinkTest < Test::Unit::TestCase
|
|
87
150
|
link3_raw = 'http://manuals.ruby-on-rails.com/read/chapter.need_a-period/103#page281'
|
88
151
|
link3_result = %{<a href="#{link3_raw}">#{link3_raw}</a>}
|
89
152
|
link4_raw = 'http://foo.example.com/controller/action?parm=value&p2=v2#anchor123'
|
90
|
-
link4_result = %{<a href="#{link4_raw}">#{CGI.escapeHTML
|
153
|
+
link4_result = %{<a href="#{CGI.escapeHTML link4_raw}">#{CGI.escapeHTML link4_raw}</a>}
|
91
154
|
link5_raw = 'http://foo.example.com:3000/controller/action'
|
92
155
|
link5_result = %{<a href="#{link5_raw}">#{link5_raw}</a>}
|
93
156
|
link6_raw = 'http://foo.example.com:3000/controller/action+pack'
|
94
157
|
link6_result = %{<a href="#{link6_raw}">#{link6_raw}</a>}
|
95
158
|
link7_raw = 'http://foo.example.com/controller/action?parm=value&p2=v2#anchor-123'
|
96
|
-
link7_result = %{<a href="#{link7_raw}">#{CGI.escapeHTML
|
159
|
+
link7_result = %{<a href="#{CGI.escapeHTML link7_raw}">#{CGI.escapeHTML link7_raw}</a>}
|
97
160
|
link8_raw = 'http://foo.example.com:3000/controller/action.html'
|
98
161
|
link8_result = %{<a href="#{link8_raw}">#{link8_raw}</a>}
|
99
162
|
link9_raw = 'http://business.timesonline.co.uk/article/0,,9065-2473189,00.html'
|
@@ -132,4 +195,24 @@ class RedcarpetAutolinkTest < Test::Unit::TestCase
|
|
132
195
|
assert_linked "#{link_result} #{link_result} #{link_result}", "#{link_raw} #{link_raw} #{link_raw}"
|
133
196
|
assert_linked '<a href="http://www.rubyonrails.com">Ruby On Rails</a>', '<a href="http://www.rubyonrails.com">Ruby On Rails</a>'
|
134
197
|
end
|
198
|
+
|
199
|
+
if "".respond_to?(:force_encoding)
|
200
|
+
def test_copies_source_encoding
|
201
|
+
str = "http://www.bash.org"
|
202
|
+
|
203
|
+
ret = Rinku.auto_link str
|
204
|
+
assert_equal str.encoding, ret.encoding
|
205
|
+
|
206
|
+
str.encode! 'binary'
|
207
|
+
|
208
|
+
ret = Rinku.auto_link str
|
209
|
+
assert_equal str.encoding, ret.encoding
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def generate_result(link_text, href = nil)
|
214
|
+
href ||= link_text
|
215
|
+
%{<a href="#{CGI::escapeHTML href}">#{CGI::escapeHTML link_text}</a>}
|
216
|
+
end
|
217
|
+
|
135
218
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rinku
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 19
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 1.0.0
|
10
|
+
version: 1.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- "Vicent Mart\xC3\xAD"
|
@@ -15,8 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-06-
|
19
|
-
default_executable:
|
18
|
+
date: 2011-06-11 00:00:00 Z
|
20
19
|
dependencies: []
|
21
20
|
|
22
21
|
description: " A fast and very smart autolinking library that\n acts as a drop-in replacement for Rails `auto_link`\n"
|
@@ -29,7 +28,6 @@ extra_rdoc_files:
|
|
29
28
|
- COPYING
|
30
29
|
files:
|
31
30
|
- COPYING
|
32
|
-
- VERSION
|
33
31
|
- README.markdown
|
34
32
|
- Rakefile
|
35
33
|
- ext/rinku/rinku.c
|
@@ -40,9 +38,9 @@ files:
|
|
40
38
|
- ext/rinku/html_autolink.c
|
41
39
|
- ext/rinku/extconf.rb
|
42
40
|
- lib/rinku.rb
|
41
|
+
- lib/rails_rinku.rb
|
43
42
|
- rinku.gemspec
|
44
43
|
- test/autolink_test.rb
|
45
|
-
has_rdoc: true
|
46
44
|
homepage: http://github.com/tanoku/rinku
|
47
45
|
licenses: []
|
48
46
|
|
@@ -72,7 +70,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
70
|
requirements: []
|
73
71
|
|
74
72
|
rubyforge_project:
|
75
|
-
rubygems_version: 1.
|
73
|
+
rubygems_version: 1.8.5
|
76
74
|
signing_key:
|
77
75
|
specification_version: 3
|
78
76
|
summary: Mostly autolinking
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
1.0.0
|