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 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 drop-in replacement for Rails `auto_link`
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
- And I'm a lazy bum, so I've copied and pasted the Rails API docs here.
25
- Yes, the Rinku API is 100% compatible.
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 = 0;
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 (np < 2)
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 = 0;
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
@@ -83,7 +83,7 @@ is_closing_a(const char *tag, size_t size)
83
83
  }
84
84
 
85
85
  static size_t
86
- skip_tags(struct buf *ob, const char *text, size_t size)
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
- const char *active_chars;
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
- switch (flags) {
125
- case AUTOLINK_EMAILS:
126
- active_chars = "<@";
127
- break;
149
+ memset(active_chars, 0x0, sizeof(active_chars));
128
150
 
129
- case AUTOLINK_URLS:
130
- active_chars = "<w:";
151
+ active_chars['<'] = AUTOLINK_ACTION_SKIP_TAG;
131
152
 
132
- case AUTOLINK_ALL:
133
- active_chars = "<@w:";
134
- break;
153
+ if (flags & AUTOLINK_EMAILS)
154
+ active_chars['@'] = AUTOLINK_ACTION_EMAIL;
135
155
 
136
- default:
137
- return;
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 && strchr(active_chars, text->data[end]) == NULL)
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
- switch (text->data[i]) {
162
- case '@':
163
- end = ups_autolink__email(&rewind, link, text->data + i, i, text->size - i);
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
- case 'w':
177
- end = ups_autolink__www(&rewind, link, text->data + i, i, text->size - i);
178
- if (end > 0) {
179
- BUFPUTSL(ob, "<a");
180
- if (link_attr) bufputs(ob, link_attr);
181
- BUFPUTSL(ob, " href=\"http://");
182
- bufput(ob, link->data, link->size);
183
- BUFPUTSL(ob, "\">");
184
- link_text_cb(ob, link, payload);
185
- BUFPUTSL(ob, "</a>");
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, "3&", &rb_text, &rb_mode, &rb_html, &rb_block);
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
- Check_Type(rb_mode, T_SYMBOL);
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, AUTOLINK_ALL, link_attr, &autolink_callback, (void*)rb_block);
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, AUTOLINK_ALL, link_attr, NULL, NULL);
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
- 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
- }
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
- rb_cRinku = rb_define_class("Rinku", rb_cObject);
83
- rb_define_singleton_method(rb_cRinku, "_auto_link", rb_rinku_autolink, -1);
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
 
@@ -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
- 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
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 = File.open('VERSION').read.strip
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
  ]
@@ -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(url)}</a>), url
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(link4_raw)}</a>}
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(link7_raw)}</a>}
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: 23
5
- prerelease: false
4
+ hash: 19
5
+ prerelease:
6
6
  segments:
7
7
  - 1
8
+ - 1
8
9
  - 0
9
- - 0
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-09 00:00:00 +02:00
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.3.7
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