rinku 1.0.0 → 1.1.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/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
|