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 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